├── .babelrc ├── .eslintignore ├── .eslintrc ├── .gitignore ├── README.md ├── architecture ├── build │ ├── css │ │ ├── .babelrc │ │ ├── build │ │ │ └── bundle.js │ │ ├── index.html │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── src │ │ │ ├── App.css │ │ │ ├── App.js │ │ │ ├── App.test.js │ │ │ ├── components │ │ │ │ ├── Copyright │ │ │ │ │ ├── Copyright.css │ │ │ │ │ ├── Copyright.js │ │ │ │ │ └── Copyright.spec.js │ │ │ │ └── IdeaButton │ │ │ │ │ ├── IdeaButton.css │ │ │ │ │ ├── IdeaButton.js │ │ │ │ │ ├── IdeaButton.spec.js │ │ │ │ │ ├── idea.png │ │ │ │ │ └── idea.svg │ │ │ ├── index.css │ │ │ └── index.js │ │ └── webpack.config.js │ ├── img │ │ ├── .babelrc │ │ ├── build │ │ │ ├── 6db3758bfc59f23bf4410c22857462c4.svg │ │ │ └── bundle.js │ │ ├── index.html │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── src │ │ │ ├── App.css │ │ │ ├── App.js │ │ │ ├── App.test.js │ │ │ ├── components │ │ │ │ ├── Copyright │ │ │ │ │ ├── Copyright.css │ │ │ │ │ ├── Copyright.js │ │ │ │ │ └── Copyright.spec.js │ │ │ │ └── IdeaButton │ │ │ │ │ ├── IdeaButton.css │ │ │ │ │ ├── IdeaButton.js │ │ │ │ │ ├── IdeaButton.spec.js │ │ │ │ │ ├── idea.png │ │ │ │ │ └── idea.svg │ │ │ ├── index.css │ │ │ └── index.js │ │ └── webpack.config.js │ ├── initial │ │ ├── .babelrc │ │ ├── build │ │ │ └── bundle.js │ │ ├── index.html │ │ ├── package-lock.json │ │ ├── package.json │ │ └── src │ │ │ ├── App.css │ │ │ ├── App.js │ │ │ ├── App.test.js │ │ │ ├── components │ │ │ ├── Copyright │ │ │ │ ├── Copyright.css │ │ │ │ ├── Copyright.js │ │ │ │ └── Copyright.spec.js │ │ │ └── IdeaButton │ │ │ │ ├── IdeaButton.css │ │ │ │ ├── IdeaButton.js │ │ │ │ ├── IdeaButton.spec.js │ │ │ │ ├── idea.png │ │ │ │ └── idea.svg │ │ │ ├── index.css │ │ │ └── index.js │ └── webpack │ │ ├── .babelrc │ │ ├── build │ │ └── bundle.js │ │ ├── index.html │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── src │ │ ├── App.css │ │ ├── App.js │ │ ├── App.test.js │ │ ├── components │ │ │ ├── Copyright │ │ │ │ ├── Copyright.css │ │ │ │ ├── Copyright.js │ │ │ │ └── Copyright.spec.js │ │ │ └── IdeaButton │ │ │ │ ├── IdeaButton.css │ │ │ │ ├── IdeaButton.js │ │ │ │ ├── IdeaButton.spec.js │ │ │ │ ├── idea.png │ │ │ │ └── idea.svg │ │ ├── index.css │ │ └── index.js │ │ └── webpack.config.js ├── component │ └── simplifying-js-component │ │ ├── .gitignore │ │ ├── README.md │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ └── manifest.json │ │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── App.test.js │ │ ├── components │ │ ├── Copyright │ │ │ ├── Copyright.css │ │ │ ├── Copyright.js │ │ │ └── Copyright.spec.js │ │ └── IdeaButton │ │ │ ├── IdeaButton.css │ │ │ ├── IdeaButton.js │ │ │ ├── IdeaButton.spec.js │ │ │ └── idea.svg │ │ ├── index.css │ │ └── index.js ├── css │ ├── animate │ │ ├── index.html │ │ ├── main.css │ │ └── open.js │ ├── index.html │ ├── initial │ │ ├── index-truncated.html │ │ ├── index.html │ │ └── main.css │ └── middle │ │ ├── index.html │ │ ├── main.css │ │ └── open.js ├── import │ ├── class │ │ └── address.js │ ├── default │ │ ├── address.js │ │ ├── list.js │ │ ├── list.spec.js │ │ └── mail.js │ ├── each │ │ ├── bill.js │ │ ├── bill.spec.js │ │ ├── name.js │ │ ├── name.spec.js │ │ ├── util.js │ │ └── util.spec.js │ └── single │ │ ├── area.js │ │ ├── bill.js │ │ ├── bill.spec.js │ │ ├── math.js │ │ ├── name.js │ │ ├── name.spec.js │ │ └── util.js └── npm │ ├── defaults │ └── package.json │ ├── save │ └── package.json │ ├── saveDev │ └── package.json │ ├── script │ ├── code │ │ └── merge.js │ └── package.json │ └── utils │ ├── clone.js │ ├── merge.js │ └── merge.spec.js ├── arrays ├── arrays │ ├── arrays.js │ └── arrays.spec.js ├── includes │ ├── greater.js │ ├── includes.js │ ├── includes.spec.js │ └── problem.js ├── push │ ├── push.js │ └── push.spec.js ├── sort │ ├── sort.js │ ├── sort.spec.js │ ├── sortMutate.js │ └── sortSpread.js └── spread │ ├── problem.js │ ├── slice.js │ ├── splice.js │ ├── spread.js │ └── spread.spec.js ├── classes ├── bind │ ├── bind.js │ ├── bind.spec.js │ ├── constructor.js │ ├── constructorArrow.js │ ├── problem.js │ └── properties.js ├── constructor ├── extend │ ├── basic.js │ ├── constructor.js │ ├── extend.js │ ├── extend.spec.js │ ├── flash.js │ └── method.js ├── generators │ ├── generators.js │ ├── generators.spec.js │ ├── problem.js │ └── simple.js ├── get │ ├── get.js │ ├── get.spec.js │ ├── invalid.js │ ├── price.js │ ├── problem.js │ └── set.js └── prototypes │ ├── class.js │ ├── prototypes.js │ └── prototypes.spec.js ├── collections ├── assign │ ├── assign.js │ ├── assign.spec.js │ ├── mutate.js │ └── problem.js ├── map │ ├── map.js │ ├── map.spec.js │ └── problem.js ├── mapSideEffects │ ├── copy.js │ ├── map.js │ ├── map.spec.js │ └── sideEffects.js ├── mapSpread │ ├── iterate.js │ ├── mapSpread.js │ ├── mapSpread.spec.js │ └── object.js ├── object │ └── object.js ├── objectSpread │ ├── objectSpread.js │ └── objectSpread.spec.js └── set │ ├── set.js │ ├── set.spec.js │ ├── unique.js │ └── unique.spec.js ├── conditionals ├── falsy │ ├── falsy.js │ ├── falsy.spec.js │ └── notFalsy.js ├── shortCircuiting │ ├── conditional.js │ ├── shortCircuiting.js │ ├── shortCircuiting.spec.js │ └── ternary.js └── ternary │ ├── if.js │ ├── ternary.js │ ├── ternary.spec.js │ └── ternaryProblem.js ├── externalData ├── async │ ├── async.js │ ├── async.spec.js │ └── catch.js ├── fetch │ ├── example.js │ ├── fetch.js │ ├── fetch.spec.js │ ├── posts.js │ └── services │ │ └── postService.js ├── local │ ├── local.js │ └── local.spec.js └── promises │ ├── problem.js │ ├── promises.js │ └── promises.spec.js ├── functions ├── arrow │ ├── arrow.js │ ├── arrow.spec.js │ ├── close.js │ └── problem.js ├── context │ ├── basic.js │ ├── context.js │ ├── context.spec.js │ ├── method.js │ └── problem.js ├── curry │ ├── curry.js │ ├── curry.spec.js │ ├── curry.spec2.js │ ├── highorder.js │ └── problem.js ├── oldTest │ ├── problem.js │ ├── problem.spec.js │ ├── routing.js │ ├── taxService.js │ ├── test.js │ └── test.spec.js ├── partial │ ├── partial.js │ ├── partial.spec.js │ ├── problem.js │ └── program.js └── test │ ├── problem.js │ ├── problem.spec.js │ ├── routing.js │ ├── taxService.js │ ├── test.js │ └── test.spec.js ├── loops ├── arrow │ ├── anonymous.js │ ├── arrow.js │ ├── arrow.spec.js │ └── full.js ├── chain │ ├── chain.js │ ├── chain.spec.js │ └── full.js ├── filter │ ├── filter.js │ ├── filter.spec.js │ └── full.js ├── for │ ├── for.js │ ├── for.spec.js │ ├── forin.js │ ├── full.js │ └── traditional.js ├── forEach │ ├── forEach.js │ ├── forEach.spec.js │ └── full.js ├── map │ ├── full.js │ ├── map.js │ └── map.spec.js ├── methods │ ├── methods.js │ ├── methods.spec.js │ └── problem.js └── reduce │ ├── map.js │ ├── mistake.js │ ├── reduce.js │ └── reduce.spec.js ├── package-lock.json ├── package.json ├── params ├── assignment │ ├── assignment.js │ └── assignment.spec.js ├── defaults │ ├── default.js │ ├── default.spec.js │ ├── more.js │ ├── problem.js │ └── simple.js ├── destructuring │ ├── alternate.js │ ├── destructuring.js │ ├── destructuring.spec.js │ └── problem.js └── rest │ ├── problem.js │ ├── rest.js │ ├── rest.spec.js │ └── simple.js ├── test └── mocha.opts └── variables ├── const ├── const.js └── const.spec.js ├── let ├── const.js ├── declaration.js ├── let.js ├── let.spec.js └── problem.js ├── literals ├── literals.js ├── literals.spec.js └── problem.js └── scope ├── curry.js ├── problem.js ├── scope.html ├── scope.js └── scope.spec.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["env"], 3 | "plugins": ["transform-async-to-generator", "transform-object-rest-spread", "transform-class-properties"], 4 | } 5 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | functions/arrow/close.js 2 | architecture/build/css/node_modules 3 | architecture/build/css/build 4 | architecture/build/img/node_modules 5 | architecture/build/img/build 6 | architecture/build/initial/node_modules 7 | architecture/build/initial/build 8 | architecture/build/webpack/node_modules 9 | architecture/build/webpack/build 10 | architecture/component/simplifying-js-component/node_modules 11 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true 4 | }, 5 | "parser": "babel-eslint", 6 | "extends": "airbnb", 7 | "globals": { 8 | "document": true, 9 | "afterEach": true, 10 | "beforeEach": true, 11 | "jest": true, 12 | "it": true, 13 | "describe": true, 14 | "expect": true, 15 | "fetch": true, 16 | "google": true 17 | }, 18 | "rules": { 19 | "arrow-parens": ["error", "as-needed"], 20 | "arrow-body-style": 0, 21 | "class-methods-use-this": 0, 22 | "import/extensions": 0, 23 | "import/no-unresolved": 0, 24 | "import/prefer-default-export": 0, 25 | "linebreak-style": 0, 26 | "no-plusplus": 0, 27 | "no-shadow": 0, 28 | "no-restricted-syntax": 0, 29 | "no-underscore-dangle": 0, 30 | "no-unused-expressions": 0, 31 | "react/jsx-filename-extension": 0, 32 | "react/prop-types": 0, 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simplifying JavaScript Code 2 | 3 | ## Tests 4 | - `npm install` 5 | - `npm test` 6 | 7 | If you want to edit code while running tests: 8 | - `npm run test:watch` 9 | -------------------------------------------------------------------------------- /architecture/build/css/.babelrc: -------------------------------------------------------------------------------- 1 | { "presets": ["env", "react"] } 2 | -------------------------------------------------------------------------------- /architecture/build/css/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Sample 5 | 6 | 7 |
8 |
9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /architecture/build/css/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "initial", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "webpack" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "babel-cli": "^6.26.0", 14 | "babel-loader": "^7.1.2", 15 | "babel-preset-env": "^1.6.1", 16 | "babel-preset-react": "^6.24.1", 17 | "css-loader": "^0.28.7", 18 | "style-loader": "^0.19.0", 19 | "webpack": "^3.8.1" 20 | }, 21 | "dependencies": { 22 | "react": "^16.1.1", 23 | "react-dom": "^16.1.1" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /architecture/build/css/src/App.css: -------------------------------------------------------------------------------- 1 | .main { 2 | width: 100%; 3 | } 4 | 5 | .app { 6 | padding: 1em; 7 | } 8 | 9 | footer { 10 | margin: 2em 0 0 0; 11 | padding: 1em; 12 | width: 100%; 13 | height: 3em; 14 | border-top: black solid 1px; 15 | } 16 | -------------------------------------------------------------------------------- /architecture/build/css/src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import './App.css'; 4 | import Copyright from './components/Copyright/Copyright'; 5 | 6 | export default function App() { 7 | return ( 8 |
9 |
10 |
11 | 12 |
13 |
14 | ); 15 | } 16 | 17 | -------------------------------------------------------------------------------- /architecture/build/css/src/App.test.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsmapr1/simplifying-js/a7ced4922664a4ea0d2b584963fd4d7aeebd7321/architecture/build/css/src/App.test.js -------------------------------------------------------------------------------- /architecture/build/css/src/components/Copyright/Copyright.css: -------------------------------------------------------------------------------- 1 | .copyright { 2 | font-size: 10px; 3 | margin: 1em; 4 | margin-left: 0; 5 | float: left; 6 | } 7 | -------------------------------------------------------------------------------- /architecture/build/css/src/components/Copyright/Copyright.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './Copyright.css'; 3 | 4 | export default function CopyrightStatement() { 5 | const year = new Date().getFullYear(); 6 | return ( 7 |
8 | Copyright {year} 9 |
10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /architecture/build/css/src/components/Copyright/Copyright.spec.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsmapr1/simplifying-js/a7ced4922664a4ea0d2b584963fd4d7aeebd7321/architecture/build/css/src/components/Copyright/Copyright.spec.js -------------------------------------------------------------------------------- /architecture/build/css/src/components/IdeaButton/IdeaButton.css: -------------------------------------------------------------------------------- 1 | .idea-button { 2 | background: #ffffff; 3 | border: black solid 1px; 4 | border-radius: 3px; 5 | width: 200px; 6 | font-size: 16px; 7 | line-height: 40px; 8 | } 9 | 10 | .idea-button:hover { 11 | cursor: pointer; 12 | } 13 | 14 | .idea-button__icon { 15 | width: 40px; 16 | height: 40px; 17 | float: left; 18 | } 19 | -------------------------------------------------------------------------------- /architecture/build/css/src/components/IdeaButton/IdeaButton.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './IdeaButton.css'; 3 | import idea from './idea.svg'; 4 | 5 | export default function IdeaButton({ handleClick, message }) { 6 | return ( 7 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /architecture/build/css/src/components/IdeaButton/IdeaButton.spec.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsmapr1/simplifying-js/a7ced4922664a4ea0d2b584963fd4d7aeebd7321/architecture/build/css/src/components/IdeaButton/IdeaButton.spec.js -------------------------------------------------------------------------------- /architecture/build/css/src/components/IdeaButton/idea.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsmapr1/simplifying-js/a7ced4922664a4ea0d2b584963fd4d7aeebd7321/architecture/build/css/src/components/IdeaButton/idea.png -------------------------------------------------------------------------------- /architecture/build/css/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /architecture/build/css/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | ReactDOM.render(, document.getElementById('root')); 6 | -------------------------------------------------------------------------------- /architecture/build/css/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | entry: './src/index.js', 5 | // START:loader 6 | module: { 7 | loaders: [ 8 | { 9 | test: /\.css$/, 10 | use: [ 11 | 'style-loader', 12 | 'css-loader', 13 | ], 14 | }, 15 | { 16 | test: /\.js?/, 17 | use: 'babel-loader', 18 | }, 19 | ], 20 | }, 21 | // END:loader 22 | output: { 23 | filename: 'build/bundle.js', 24 | path: path.resolve(__dirname), 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /architecture/build/img/.babelrc: -------------------------------------------------------------------------------- 1 | { "presets": ["env", "react"] } 2 | -------------------------------------------------------------------------------- /architecture/build/img/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Sample 5 | 6 | 7 |
8 |
9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /architecture/build/img/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "initial", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "webpack" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "babel-cli": "^6.26.0", 14 | "babel-loader": "^7.1.2", 15 | "babel-preset-env": "^1.6.1", 16 | "babel-preset-react": "^6.24.1", 17 | "css-loader": "^0.28.7", 18 | "file-loader": "^1.1.5", 19 | "style-loader": "^0.19.0", 20 | "webpack": "^3.8.1" 21 | }, 22 | "dependencies": { 23 | "react": "^16.1.1", 24 | "react-dom": "^16.1.1" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /architecture/build/img/src/App.css: -------------------------------------------------------------------------------- 1 | .main { 2 | width: 100%; 3 | } 4 | 5 | .app { 6 | padding: 1em; 7 | } 8 | 9 | footer { 10 | margin: 2em 0 0 0; 11 | padding: 1em; 12 | width: 100%; 13 | height: 3em; 14 | border-top: black solid 1px; 15 | } 16 | -------------------------------------------------------------------------------- /architecture/build/img/src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import './App.css'; 4 | import IdeaButton from './components/IdeaButton/IdeaButton'; 5 | import Copyright from './components/Copyright/Copyright'; 6 | 7 | function logIdea() { 8 | console.log('Someone had an idea!'); 9 | } 10 | 11 | export default function App() { 12 | return ( 13 |
14 |
15 | 19 |
20 |
21 | 22 | 26 |
27 |
28 | ); 29 | } 30 | 31 | -------------------------------------------------------------------------------- /architecture/build/img/src/App.test.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsmapr1/simplifying-js/a7ced4922664a4ea0d2b584963fd4d7aeebd7321/architecture/build/img/src/App.test.js -------------------------------------------------------------------------------- /architecture/build/img/src/components/Copyright/Copyright.css: -------------------------------------------------------------------------------- 1 | .copyright { 2 | font-size: 10px; 3 | margin: 1em; 4 | margin-left: 0; 5 | float: left; 6 | } 7 | -------------------------------------------------------------------------------- /architecture/build/img/src/components/Copyright/Copyright.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './Copyright.css'; 3 | 4 | export default function CopyrightStatement() { 5 | const year = new Date().getFullYear(); 6 | return ( 7 |
8 | Copyright {year} 9 |
10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /architecture/build/img/src/components/Copyright/Copyright.spec.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsmapr1/simplifying-js/a7ced4922664a4ea0d2b584963fd4d7aeebd7321/architecture/build/img/src/components/Copyright/Copyright.spec.js -------------------------------------------------------------------------------- /architecture/build/img/src/components/IdeaButton/IdeaButton.css: -------------------------------------------------------------------------------- 1 | .idea-button { 2 | background: #ffffff; 3 | border: black solid 1px; 4 | border-radius: 3px; 5 | width: 200px; 6 | font-size: 16px; 7 | line-height: 40px; 8 | } 9 | 10 | .idea-button:hover { 11 | cursor: pointer; 12 | } 13 | 14 | .idea-button__icon { 15 | width: 40px; 16 | height: 40px; 17 | float: left; 18 | } 19 | -------------------------------------------------------------------------------- /architecture/build/img/src/components/IdeaButton/IdeaButton.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './IdeaButton.css'; 3 | import idea from './idea.svg'; 4 | 5 | export default function IdeaButton({ handleClick, message }) { 6 | return ( 7 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /architecture/build/img/src/components/IdeaButton/IdeaButton.spec.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsmapr1/simplifying-js/a7ced4922664a4ea0d2b584963fd4d7aeebd7321/architecture/build/img/src/components/IdeaButton/IdeaButton.spec.js -------------------------------------------------------------------------------- /architecture/build/img/src/components/IdeaButton/idea.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsmapr1/simplifying-js/a7ced4922664a4ea0d2b584963fd4d7aeebd7321/architecture/build/img/src/components/IdeaButton/idea.png -------------------------------------------------------------------------------- /architecture/build/img/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /architecture/build/img/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | ReactDOM.render(, document.getElementById('root')); 6 | -------------------------------------------------------------------------------- /architecture/build/img/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | entry: './src/index.js', 5 | // START:loader 6 | module: { 7 | loaders: [ 8 | { 9 | test: /\.svg?/, 10 | use: [ 11 | { 12 | loader: 'file-loader', 13 | options: { 14 | outputPath: 'build/', 15 | }, 16 | }, 17 | ], 18 | }, 19 | { 20 | test: /\.css$/, 21 | use: [ 22 | 'style-loader', 23 | 'css-loader', 24 | ], 25 | }, 26 | { 27 | test: /\.js?/, 28 | use: 'babel-loader', 29 | }, 30 | ], 31 | }, 32 | // END:loader 33 | output: { 34 | filename: 'build/bundle.js', 35 | path: path.resolve(__dirname), 36 | }, 37 | }; 38 | -------------------------------------------------------------------------------- /architecture/build/initial/.babelrc: -------------------------------------------------------------------------------- 1 | { "presets": ["env", "react"] } 2 | -------------------------------------------------------------------------------- /architecture/build/initial/build/bundle.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _react = require('react'); 4 | 5 | var _react2 = _interopRequireDefault(_react); 6 | 7 | var _reactDom = require('react-dom'); 8 | 9 | var _reactDom2 = _interopRequireDefault(_reactDom); 10 | 11 | var _App = require('./App'); 12 | 13 | var _App2 = _interopRequireDefault(_App); 14 | 15 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 16 | 17 | _reactDom2.default.render(_react2.default.createElement(_App2.default, null), document.getElementById('root')); 18 | -------------------------------------------------------------------------------- /architecture/build/initial/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Sample 5 | 6 | 7 |
8 |
9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /architecture/build/initial/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "initial", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "babel src/index.js -o build/bundle.js" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "babel-cli": "^6.26.0", 14 | "babel-preset-env": "^1.6.1", 15 | "babel-preset-react": "^6.24.1" 16 | }, 17 | "dependencies": { 18 | "react": "^16.1.1", 19 | "react-dom": "^16.1.1" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /architecture/build/initial/src/App.css: -------------------------------------------------------------------------------- 1 | .main { 2 | width: 100%; 3 | } 4 | 5 | .app { 6 | padding: 1em; 7 | } 8 | 9 | footer { 10 | margin: 2em 0 0 0; 11 | padding: 1em; 12 | width: 100%; 13 | height: 3em; 14 | border-top: black solid 1px; 15 | } 16 | -------------------------------------------------------------------------------- /architecture/build/initial/src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Copyright from './components/Copyright/Copyright'; 4 | 5 | export default function App() { 6 | return ( 7 |
8 |
9 | 10 |
11 |
12 | ); 13 | } 14 | 15 | -------------------------------------------------------------------------------- /architecture/build/initial/src/App.test.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsmapr1/simplifying-js/a7ced4922664a4ea0d2b584963fd4d7aeebd7321/architecture/build/initial/src/App.test.js -------------------------------------------------------------------------------- /architecture/build/initial/src/components/Copyright/Copyright.css: -------------------------------------------------------------------------------- 1 | .copyright { 2 | font-size: 10px; 3 | margin: 1em; 4 | margin-left: 0; 5 | float: left; 6 | } 7 | -------------------------------------------------------------------------------- /architecture/build/initial/src/components/Copyright/Copyright.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default function CopyrightStatement() { 4 | const year = new Date().getFullYear(); 5 | return ( 6 |
7 | Copyright {year} 8 |
9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /architecture/build/initial/src/components/Copyright/Copyright.spec.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsmapr1/simplifying-js/a7ced4922664a4ea0d2b584963fd4d7aeebd7321/architecture/build/initial/src/components/Copyright/Copyright.spec.js -------------------------------------------------------------------------------- /architecture/build/initial/src/components/IdeaButton/IdeaButton.css: -------------------------------------------------------------------------------- 1 | .idea-button { 2 | background: #ffffff; 3 | border: black solid 1px; 4 | border-radius: 3px; 5 | width: 200px; 6 | font-size: 16px; 7 | line-height: 40px; 8 | } 9 | 10 | .idea-button:hover { 11 | cursor: pointer; 12 | } 13 | 14 | .idea-button__icon { 15 | width: 40px; 16 | height: 40px; 17 | float: left; 18 | } 19 | -------------------------------------------------------------------------------- /architecture/build/initial/src/components/IdeaButton/IdeaButton.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './IdeaButton.css'; 3 | import idea from './idea.svg'; 4 | 5 | export default function IdeaButton({ handleClick, message }) { 6 | return ( 7 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /architecture/build/initial/src/components/IdeaButton/IdeaButton.spec.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsmapr1/simplifying-js/a7ced4922664a4ea0d2b584963fd4d7aeebd7321/architecture/build/initial/src/components/IdeaButton/IdeaButton.spec.js -------------------------------------------------------------------------------- /architecture/build/initial/src/components/IdeaButton/idea.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsmapr1/simplifying-js/a7ced4922664a4ea0d2b584963fd4d7aeebd7321/architecture/build/initial/src/components/IdeaButton/idea.png -------------------------------------------------------------------------------- /architecture/build/initial/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /architecture/build/initial/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | ReactDOM.render(, document.getElementById('root')); 6 | -------------------------------------------------------------------------------- /architecture/build/webpack/.babelrc: -------------------------------------------------------------------------------- 1 | { "presets": ["env", "react"] } 2 | -------------------------------------------------------------------------------- /architecture/build/webpack/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Sample 5 | 6 | 7 |
8 |
9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /architecture/build/webpack/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "initial", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "webpack" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "babel-cli": "^6.26.0", 14 | "babel-loader": "^7.1.2", 15 | "babel-preset-env": "^1.6.1", 16 | "babel-preset-react": "^6.24.1", 17 | "webpack": "^3.8.1" 18 | }, 19 | "dependencies": { 20 | "react": "^16.1.1", 21 | "react-dom": "^16.1.1" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /architecture/build/webpack/src/App.css: -------------------------------------------------------------------------------- 1 | .main { 2 | width: 100%; 3 | } 4 | 5 | .app { 6 | padding: 1em; 7 | } 8 | 9 | footer { 10 | margin: 2em 0 0 0; 11 | padding: 1em; 12 | width: 100%; 13 | height: 3em; 14 | border-top: black solid 1px; 15 | } 16 | -------------------------------------------------------------------------------- /architecture/build/webpack/src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Copyright from './components/Copyright/Copyright'; 4 | 5 | export default function App() { 6 | return ( 7 |
8 |
9 |
10 | 11 |
12 |
13 | ); 14 | } 15 | 16 | -------------------------------------------------------------------------------- /architecture/build/webpack/src/App.test.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsmapr1/simplifying-js/a7ced4922664a4ea0d2b584963fd4d7aeebd7321/architecture/build/webpack/src/App.test.js -------------------------------------------------------------------------------- /architecture/build/webpack/src/components/Copyright/Copyright.css: -------------------------------------------------------------------------------- 1 | .copyright { 2 | font-size: 10px; 3 | margin: 1em; 4 | margin-left: 0; 5 | float: left; 6 | } 7 | -------------------------------------------------------------------------------- /architecture/build/webpack/src/components/Copyright/Copyright.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default function CopyrightStatement() { 4 | const year = new Date().getFullYear(); 5 | return ( 6 |
7 | Copyright {year} 8 |
9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /architecture/build/webpack/src/components/Copyright/Copyright.spec.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsmapr1/simplifying-js/a7ced4922664a4ea0d2b584963fd4d7aeebd7321/architecture/build/webpack/src/components/Copyright/Copyright.spec.js -------------------------------------------------------------------------------- /architecture/build/webpack/src/components/IdeaButton/IdeaButton.css: -------------------------------------------------------------------------------- 1 | .idea-button { 2 | background: #ffffff; 3 | border: black solid 1px; 4 | border-radius: 3px; 5 | width: 200px; 6 | font-size: 16px; 7 | line-height: 40px; 8 | } 9 | 10 | .idea-button:hover { 11 | cursor: pointer; 12 | } 13 | 14 | .idea-button__icon { 15 | width: 40px; 16 | height: 40px; 17 | float: left; 18 | } 19 | -------------------------------------------------------------------------------- /architecture/build/webpack/src/components/IdeaButton/IdeaButton.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './IdeaButton.css'; 3 | import idea from './idea.svg'; 4 | 5 | export default function IdeaButton({ handleClick, message }) { 6 | return ( 7 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /architecture/build/webpack/src/components/IdeaButton/IdeaButton.spec.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsmapr1/simplifying-js/a7ced4922664a4ea0d2b584963fd4d7aeebd7321/architecture/build/webpack/src/components/IdeaButton/IdeaButton.spec.js -------------------------------------------------------------------------------- /architecture/build/webpack/src/components/IdeaButton/idea.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsmapr1/simplifying-js/a7ced4922664a4ea0d2b584963fd4d7aeebd7321/architecture/build/webpack/src/components/IdeaButton/idea.png -------------------------------------------------------------------------------- /architecture/build/webpack/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /architecture/build/webpack/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | ReactDOM.render(, document.getElementById('root')); 6 | -------------------------------------------------------------------------------- /architecture/build/webpack/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | entry: './src/index.js', 5 | module: { 6 | loaders: [ 7 | { 8 | test: /\.js?/, 9 | use: 'babel-loader', 10 | }, 11 | ], 12 | }, 13 | output: { 14 | filename: 'build/bundle.js', 15 | path: path.resolve(__dirname), 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /architecture/component/simplifying-js-component/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /architecture/component/simplifying-js-component/README.md: -------------------------------------------------------------------------------- 1 | Try it out! Go to a terminal and do the following: 2 | 3 | 1. `npm install` 4 | 2. `npm start` 5 | 3. Open a browser to [localhost:3000](localhost:3000) 6 | -------------------------------------------------------------------------------- /architecture/component/simplifying-js-component/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simplifying-js-component", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "react": "^16.0.0", 7 | "react-dom": "^16.0.0", 8 | "react-scripts": "1.0.17" 9 | }, 10 | "scripts": { 11 | "start": "react-scripts start", 12 | "build": "react-scripts build", 13 | "test": "react-scripts test --env=jsdom", 14 | "eject": "react-scripts eject" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /architecture/component/simplifying-js-component/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsmapr1/simplifying-js/a7ced4922664a4ea0d2b584963fd4d7aeebd7321/architecture/component/simplifying-js-component/public/favicon.ico -------------------------------------------------------------------------------- /architecture/component/simplifying-js-component/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 22 | Sample 23 | 24 | 25 | 28 |
29 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /architecture/component/simplifying-js-component/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /architecture/component/simplifying-js-component/src/App.css: -------------------------------------------------------------------------------- 1 | .main { 2 | width: 100%; 3 | } 4 | 5 | .app { 6 | padding: 1em; 7 | } 8 | 9 | footer { 10 | margin: 2em 0 0 0; 11 | padding: 1em; 12 | width: 100%; 13 | height: 3em; 14 | border-top: black solid 1px; 15 | } 16 | -------------------------------------------------------------------------------- /architecture/component/simplifying-js-component/src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import './App.css'; 4 | import IdeaButton from './components/IdeaButton/IdeaButton'; 5 | import Copyright from './components/Copyright/Copyright'; 6 | 7 | function logIdea() { 8 | console.log('Someone had an idea!'); 9 | } 10 | export default function App() { 11 | return ( 12 |
13 |
14 | 18 |
19 |
20 | 21 | 25 |
26 |
27 | ); 28 | } 29 | 30 | -------------------------------------------------------------------------------- /architecture/component/simplifying-js-component/src/App.test.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsmapr1/simplifying-js/a7ced4922664a4ea0d2b584963fd4d7aeebd7321/architecture/component/simplifying-js-component/src/App.test.js -------------------------------------------------------------------------------- /architecture/component/simplifying-js-component/src/components/Copyright/Copyright.css: -------------------------------------------------------------------------------- 1 | .copyright { 2 | font-size: 10px; 3 | margin: 1em 1em 1em 0; 4 | float: left; 5 | } 6 | -------------------------------------------------------------------------------- /architecture/component/simplifying-js-component/src/components/Copyright/Copyright.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './Copyright.css'; 3 | 4 | export default function CopyrightStatement() { 5 | const year = new Date().getFullYear(); 6 | return ( 7 |
8 | Copyright {year} 9 |
10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /architecture/component/simplifying-js-component/src/components/Copyright/Copyright.spec.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsmapr1/simplifying-js/a7ced4922664a4ea0d2b584963fd4d7aeebd7321/architecture/component/simplifying-js-component/src/components/Copyright/Copyright.spec.js -------------------------------------------------------------------------------- /architecture/component/simplifying-js-component/src/components/IdeaButton/IdeaButton.css: -------------------------------------------------------------------------------- 1 | .idea-button { 2 | background: #ffffff; 3 | border: black solid 1px; 4 | border-radius: 3px; 5 | width: 200px; 6 | font-size: 16px; 7 | line-height: 40px; 8 | } 9 | 10 | .idea-button:hover { 11 | cursor: pointer; 12 | } 13 | 14 | .idea-button__icon { 15 | width: 40px; 16 | height: 40px; 17 | float: left; 18 | } 19 | -------------------------------------------------------------------------------- /architecture/component/simplifying-js-component/src/components/IdeaButton/IdeaButton.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './IdeaButton.css'; 3 | import idea from './idea.svg'; 4 | 5 | export default function IdeaButton({ handleClick, message }) { 6 | return ( 7 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /architecture/component/simplifying-js-component/src/components/IdeaButton/IdeaButton.spec.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsmapr1/simplifying-js/a7ced4922664a4ea0d2b584963fd4d7aeebd7321/architecture/component/simplifying-js-component/src/components/IdeaButton/IdeaButton.spec.js -------------------------------------------------------------------------------- /architecture/component/simplifying-js-component/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /architecture/component/simplifying-js-component/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | ReactDOM.render(, document.getElementById('root')); 6 | -------------------------------------------------------------------------------- /architecture/css/animate/main.css: -------------------------------------------------------------------------------- 1 | .main { 2 | width: 1000px; 3 | margin: 0 auto; 4 | overflow: hidden; 5 | position: relative; 6 | } 7 | 8 | button { 9 | border: black solid 1px; 10 | background: #ffffff; 11 | } 12 | 13 | .menu { 14 | width: 300px; 15 | padding: 0 2em; 16 | float: right; 17 | border: black solid 1px; 18 | position: absolute; 19 | top: 0; 20 | right: 0; 21 | height: calc(100% - 2px); 22 | background: #ffffff; 23 | } 24 | /* //START:transition */ 25 | .menu.display { 26 | /* Other styles */ 27 | transform: translateX(0); 28 | transition-property: transform; 29 | transition-duration: 600ms; 30 | transition-timing-function: linear; 31 | } 32 | /* //END:transition */ 33 | 34 | /* //START:short */ 35 | .menu { 36 | /* Other styles */ 37 | transform: translateX(calc(300px + 4em + 2px)); 38 | transition: all 600ms linear; 39 | } 40 | /* //END:short */ 41 | -------------------------------------------------------------------------------- /architecture/css/animate/open.js: -------------------------------------------------------------------------------- 1 | const sidebar = document.getElementById('sidebar'); 2 | document.getElementById('show') 3 | .addEventListener('click', () => { 4 | sidebar.classList.toggle('display'); 5 | }); 6 | -------------------------------------------------------------------------------- /architecture/css/initial/index-truncated.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 |

Moby Dick

9 | 10 | 17 |
18 |

19 | Call me Ishmael. 20 | 21 |

22 |
23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /architecture/css/initial/main.css: -------------------------------------------------------------------------------- 1 | .main { 2 | width: 1000px; 3 | margin: 0 auto; 4 | overflow: hidden; 5 | position: relative; 6 | } 7 | 8 | button { 9 | border: black solid 1px; 10 | background: #ffffff; 11 | } 12 | 13 | .menu { 14 | width: 300px; 15 | padding: 0 2em; 16 | float: right; 17 | border: black solid 1px; 18 | position: absolute; 19 | top: 0; 20 | right: 0; 21 | height: calc(100% - 2px); 22 | background: #ffffff; 23 | } 24 | -------------------------------------------------------------------------------- /architecture/css/middle/main.css: -------------------------------------------------------------------------------- 1 | .main { 2 | width: 1000px; 3 | margin: 0 auto; 4 | overflow: hidden; 5 | position: relative; 6 | } 7 | 8 | button { 9 | border: black solid 1px; 10 | background: #ffffff; 11 | } 12 | .menu { 13 | width: 300px; 14 | padding: 0 2em; 15 | float: right; 16 | border: black solid 1px; 17 | position: absolute; 18 | top: 0; 19 | right: 0; 20 | height: calc(100% - 2px); 21 | background: #ffffff; 22 | 23 | } 24 | /* //START:transform */ 25 | .menu { 26 | /* Other styles from before */ 27 | transform: translateX(calc(300px + 4em + 2px)); 28 | } 29 | .menu.display { 30 | transform: translateX(0); 31 | } 32 | /* //END:transform */ 33 | -------------------------------------------------------------------------------- /architecture/css/middle/open.js: -------------------------------------------------------------------------------- 1 | const sidebar = document.getElementById('sidebar'); 2 | document.getElementById('show') 3 | .addEventListener('click', () => { 4 | sidebar.classList.toggle('display'); 5 | }); 6 | -------------------------------------------------------------------------------- /architecture/import/class/address.js: -------------------------------------------------------------------------------- 1 | import { capitalize } from '../single/util'; 2 | 3 | export default class Address { 4 | constructor(address) { 5 | this.address = address; 6 | } 7 | 8 | normalize() { 9 | const street = this.parseStreet(this.address); 10 | const city = this.address.city; 11 | const region = this.parseRegion(this.address); 12 | return `${street} ${city}, ${region}`; 13 | } 14 | 15 | parseStreet({ street }) { 16 | return street.split(' ') 17 | .map(part => capitalize(part)) 18 | .join(' '); 19 | } 20 | 21 | parseRegion(address) { 22 | const region = address.state || address.providence || ''; 23 | return region.toUpperCase(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /architecture/import/default/address.js: -------------------------------------------------------------------------------- 1 | import { capitalize } from '../single/util'; 2 | 3 | export function parseRegion(address) { 4 | const region = address.state || address.providence || ''; 5 | return region.toUpperCase(); 6 | } 7 | 8 | export function parseStreet({ street }) { 9 | return street.split(' ') 10 | .map(part => capitalize(part)) 11 | .join(' '); 12 | } 13 | 14 | export default function normalize(address) { 15 | const street = parseStreet(address); 16 | const city = address.city; 17 | const region = parseRegion(address); 18 | return `${street} ${city}, ${region}`; 19 | } 20 | -------------------------------------------------------------------------------- /architecture/import/default/list.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-param-reassign, no-unused-vars */ 2 | // # START:code 3 | import normalize, { parseRegion } from './address'; 4 | 5 | function getAddress(user) { 6 | return normalize(user.address); 7 | } 8 | 9 | export function getAddressByRegion(users) { 10 | return users.reduce((regions, user) => { 11 | const { address } = user; 12 | const region = parseRegion(address); 13 | const addresses = regions[region] || []; 14 | regions[region] = [...addresses, normalize(address)]; 15 | return regions; 16 | }, {}); 17 | } 18 | 19 | const bars = [ 20 | { 21 | name: 'Saint Vitus', 22 | address: { 23 | street: '1120 manhattan ave', 24 | city: 'Brooklyn', 25 | state: 'NY', 26 | }, 27 | }, 28 | ]; 29 | getAddressByRegion(bars); 30 | // { 31 | // NY: ["1120 Manhattan Ave Brooklyn, NY"] 32 | // } 33 | // # END:code 34 | -------------------------------------------------------------------------------- /architecture/import/default/list.spec.js: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | import { getAddressByRegion } from './list'; 3 | 4 | const clubs = [ 5 | { 6 | name: 'CBGB', 7 | address: { 8 | street: '315 bowery', 9 | city: 'New York', 10 | state: 'ny', 11 | }, 12 | }, 13 | { 14 | name: 'Saint Vitus', 15 | address: { 16 | street: '1120 manhattan ave', 17 | city: 'Brooklyn', 18 | state: 'NY', 19 | }, 20 | }, 21 | ]; 22 | 23 | describe('list', () => { 24 | it('should compile clubs', () => { 25 | expect(getAddressByRegion(clubs)).toEqual({ 26 | NY: [ 27 | '315 Bowery New York, NY', 28 | '1120 Manhattan Ave Brooklyn, NY', 29 | ], 30 | }); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /architecture/import/default/mail.js: -------------------------------------------------------------------------------- 1 | import normalize from './address'; 2 | 3 | function getAddress(user) { 4 | return normalize(user.address); 5 | } 6 | 7 | export default getAddress; 8 | -------------------------------------------------------------------------------- /architecture/import/each/bill.js: -------------------------------------------------------------------------------- 1 | import { 2 | capitalize, 3 | roundToDecimalPlace as round, 4 | } from './util'; 5 | 6 | function giveTotal(name, total) { 7 | return `${capitalize(name)}, your total is: ${round(total)}`; 8 | } 9 | 10 | giveTotal('sara', 10.3333333); 11 | // "Sara, your total is: 10.33" 12 | 13 | export { giveTotal }; 14 | -------------------------------------------------------------------------------- /architecture/import/each/bill.spec.js: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | import { giveTotal } from './bill'; 3 | 4 | describe('bill', () => { 5 | it('should give total', () => { 6 | expect(giveTotal('sara', 10.33333)) 7 | .toEqual('Sara, your total is: 10.33'); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /architecture/import/each/name.js: -------------------------------------------------------------------------------- 1 | import * as utils from './util'; 2 | 3 | function greet(name) { 4 | return `Hello, ${utils.capitalize(name)}!`; 5 | } 6 | 7 | greet('ashley'); 8 | // Hello, Ashley! 9 | 10 | export { greet }; 11 | -------------------------------------------------------------------------------- /architecture/import/each/name.spec.js: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | import { greet } from './name'; 3 | 4 | describe('name', () => { 5 | it('should capitalize name', () => { 6 | expect(greet('ashley')).toEqual('Hello, Ashley!'); 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /architecture/import/each/util.js: -------------------------------------------------------------------------------- 1 | function getPower(decimalPlaces) { 2 | return 10 ** decimalPlaces; 3 | } 4 | 5 | export function capitalize(word) { 6 | return word[0].toUpperCase() + word.slice(1); 7 | } 8 | 9 | export function roundToDecimalPlace(number, decimalPlaces = 2) { 10 | const round = getPower(decimalPlaces); 11 | return Math.round(number * round) / round; 12 | } 13 | -------------------------------------------------------------------------------- /architecture/import/each/util.spec.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsmapr1/simplifying-js/a7ced4922664a4ea0d2b584963fd4d7aeebd7321/architecture/import/each/util.spec.js -------------------------------------------------------------------------------- /architecture/import/single/area.js: -------------------------------------------------------------------------------- 1 | import { PI } from './math'; 2 | 3 | function area(radius) { 4 | return PI * (radius ** 2); 5 | } 6 | 7 | export { area }; 8 | -------------------------------------------------------------------------------- /architecture/import/single/bill.js: -------------------------------------------------------------------------------- 1 | import { capitalize, roundToDecimalPlace } from './util'; 2 | 3 | function giveTotal(name, total) { 4 | return `${capitalize(name)}, your total is: ${roundToDecimalPlace(total)}`; 5 | } 6 | 7 | giveTotal('sara', 10.3333333); 8 | // "Sara, your total is: 10.33" 9 | 10 | export { giveTotal }; 11 | -------------------------------------------------------------------------------- /architecture/import/single/bill.spec.js: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | import { giveTotal } from './bill'; 3 | 4 | describe('bill', () => { 5 | it('should give total', () => { 6 | expect(giveTotal('sara', 10.33333)) 7 | .toEqual('Sara, your total is: 10.33'); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /architecture/import/single/math.js: -------------------------------------------------------------------------------- 1 | const PI = 3.14; 2 | const E = 2.71828; 3 | 4 | export { E, PI }; 5 | -------------------------------------------------------------------------------- /architecture/import/single/name.js: -------------------------------------------------------------------------------- 1 | import { capitalize } from './util'; 2 | 3 | function greet(name) { 4 | return `Hello, ${capitalize(name)}!`; 5 | } 6 | 7 | greet('ashley'); 8 | // Hello, Ashley! 9 | 10 | export { greet }; 11 | -------------------------------------------------------------------------------- /architecture/import/single/name.spec.js: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | import { greet } from './name'; 3 | 4 | describe('name', () => { 5 | it('should capitalize name', () => { 6 | expect(greet('ashley')).toEqual('Hello, Ashley!'); 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /architecture/import/single/util.js: -------------------------------------------------------------------------------- 1 | function getPower(decimalPlaces) { 2 | return 10 ** decimalPlaces; 3 | } 4 | 5 | function capitalize(word) { 6 | return word[0].toUpperCase() + word.slice(1); 7 | } 8 | 9 | function roundToDecimalPlace(number, decimalPlaces = 2) { 10 | const round = getPower(decimalPlaces); 11 | return Math.round(number * round) / round; 12 | } 13 | 14 | export { capitalize, roundToDecimalPlace }; 15 | -------------------------------------------------------------------------------- /architecture/npm/defaults/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC" 11 | } 12 | -------------------------------------------------------------------------------- /architecture/npm/save/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "lodash": "^4.17.4" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /architecture/npm/saveDev/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "lodash": "^4.17.4" 13 | }, 14 | "devDependencies": { 15 | "prettier": "^1.8.2" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /architecture/npm/script/code/merge.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-duplicates */ 2 | import lodash from 'lodash'; 3 | import { fromPairs } from 'lodash'; 4 | 5 | export function mapToObject(map) { 6 | return fromPairs([...map]); 7 | } 8 | 9 | export function objectToMap(object) { 10 | const pairs = lodash.toPairs(object); 11 | return new Map(pairs); 12 | } 13 | -------------------------------------------------------------------------------- /architecture/npm/script/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "clean": "prettier --tab-width=4 --write ./code/*.js" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "lodash": "^4.17.4" 13 | }, 14 | "devDependencies": { 15 | "prettier": "^1.8.2" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /architecture/npm/utils/clone.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsmapr1/simplifying-js/a7ced4922664a4ea0d2b584963fd4d7aeebd7321/architecture/npm/utils/clone.js -------------------------------------------------------------------------------- /architecture/npm/utils/merge.js: -------------------------------------------------------------------------------- 1 | import lodash, { fromPairs } from 'lodash'; 2 | 3 | export function mapToObject(map) { 4 | return fromPairs([...map]); 5 | } 6 | 7 | export function objectToMap(object) { 8 | const pairs = lodash.toPairs(object); 9 | return new Map(pairs); 10 | } 11 | -------------------------------------------------------------------------------- /architecture/npm/utils/merge.spec.js: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | import { mapToObject, objectToMap } from './merge'; 3 | 4 | describe('utilities', () => { 5 | it('should convert map to object', () => { 6 | const simple = new Map().set('name', 'joe'); 7 | expect(mapToObject(simple)).toEqual({ name: 'joe' }); 8 | }); 9 | it('should convert object to map', () => { 10 | const person = { name: 'joe' }; 11 | const simple = new Map().set('name', 'joe'); 12 | expect(objectToMap(person)).toEqual(simple); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /arrays/arrays/arrays.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-restricted-syntax */ 2 | 3 | // # START:sort 4 | const team = [ 5 | 'Joe', 6 | 'Dyan', 7 | 'Bea', 8 | 'Theo', 9 | ]; 10 | 11 | function alphabetizeTeam(team) { 12 | return [...team].sort(); 13 | // ['Bea', 'Dyan', 'Joe', 'Theo'] 14 | } 15 | // # END:sort 16 | 17 | function getTotalStats() { 18 | // # START:loop 19 | 20 | const game1 = { 21 | player: 'Jim Jonas', 22 | hits: 2, 23 | runs: 1, 24 | errors: 0, 25 | }; 26 | 27 | const game2 = { 28 | player: 'Jim Jonas', 29 | hits: 3, 30 | runs: 0, 31 | errors: 1, 32 | }; 33 | 34 | const total = {}; 35 | 36 | const stats = Object.keys(game1); 37 | for (let i = 0; i < stats.length; i++) { 38 | const stat = stats[i]; 39 | if (stat !== 'player') { 40 | total[stat] = game1[stat] + game2[stat]; 41 | } 42 | } 43 | 44 | // { 45 | // hits: 5, 46 | // runs: 1, 47 | // errors: 1 48 | // } 49 | 50 | // # END:loop 51 | return total; 52 | } 53 | 54 | // # START:filter 55 | const staff = [ 56 | { 57 | name: 'Wesley', 58 | position: 'musician', 59 | }, 60 | { 61 | name: 'Davis', 62 | position: 'engineer', 63 | }, 64 | ]; 65 | 66 | function getMusicians(staff) { 67 | return staff.filter(member => member.position === 'musician'); 68 | // [{name: 'Wesley', position: 'musician'}] 69 | } 70 | // # END:filter 71 | 72 | // # START:object 73 | const dog = { 74 | name: 'Don', 75 | color: 'black', 76 | }; 77 | 78 | dog.name; 79 | // Don 80 | 81 | // # END:object 82 | 83 | // # START:pair 84 | const dogPair = [ 85 | ['name', 'Don'], 86 | ['color', 'black'], 87 | ]; 88 | 89 | function getName(dogPair) { 90 | return dogPair.find(attribute => { 91 | return attribute[0] === 'name'; 92 | })[1]; 93 | } 94 | // # END:pair 95 | 96 | export { 97 | alphabetizeTeam, 98 | dogPair, 99 | getMusicians, 100 | getName, 101 | getTotalStats, 102 | staff, 103 | team, 104 | }; 105 | -------------------------------------------------------------------------------- /arrays/arrays/arrays.spec.js: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | import { 3 | alphabetizeTeam, 4 | dogPair, 5 | getMusicians, 6 | getName, 7 | getTotalStats, 8 | staff, 9 | team, 10 | } from './arrays'; 11 | 12 | describe('array actions', () => { 13 | it('should alphabetize team', () => { 14 | expect(alphabetizeTeam(team)).toEqual([ 15 | 'Bea', 16 | 'Dyan', 17 | 'Joe', 18 | 'Theo', 19 | ]); 20 | }); 21 | 22 | it('should get musicians', () => { 23 | expect(getMusicians(staff)).toEqual([ 24 | { 25 | name: 'Wesley', 26 | position: 'musician', 27 | }, 28 | ]); 29 | }); 30 | 31 | it('should get name', () => { 32 | expect(getName(dogPair)).toEqual('Don'); 33 | }); 34 | 35 | it('should calculate stats', () => { 36 | expect(getTotalStats()).toEqual({ 37 | hits: 5, 38 | runs: 1, 39 | errors: 1, 40 | }); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /arrays/includes/greater.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | 3 | // # START:index 4 | const sections = ['contact', 'shipping']; 5 | 6 | function displayShipping(sections) { 7 | return sections.indexOf('shipping') > -1; 8 | } 9 | 10 | // true 11 | 12 | // # END:index 13 | 14 | export { displayShipping }; 15 | -------------------------------------------------------------------------------- /arrays/includes/includes.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | 3 | // # START:includes 4 | const sections = ['contact', 'shipping']; 5 | 6 | function displayShipping(sections) { 7 | return sections.includes('shipping'); 8 | } 9 | // # END:includes 10 | 11 | export { displayShipping }; 12 | -------------------------------------------------------------------------------- /arrays/includes/includes.spec.js: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | import { 3 | displayShipping, 4 | } from './includes'; 5 | 6 | import { 7 | displayShipping as displayShippingIndex, 8 | } from './greater'; 9 | 10 | describe('includes actions', () => { 11 | it('should display shipping using index', () => { 12 | const sections = ['shipping', 'address']; 13 | expect(displayShippingIndex(sections)).toEqual(true); 14 | }); 15 | 16 | it('should not display shipping using index', () => { 17 | const sections = ['contact', 'address']; 18 | expect(displayShippingIndex(sections)).toEqual(false); 19 | }); 20 | 21 | it('should display shipping', () => { 22 | const sections = ['shipping', 'address']; 23 | expect(displayShipping(sections)).toEqual(true); 24 | }); 25 | 26 | it('should not display shipping', () => { 27 | const sections = ['contact', 'address']; 28 | expect(displayShipping(sections)).toEqual(false); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /arrays/includes/problem.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | // # START:noIndex 3 | const sections = ['shipping']; 4 | 5 | function displayShipping(sections) { 6 | if (sections.indexOf('shipping')) { 7 | return true; 8 | } 9 | return false; 10 | } 11 | 12 | // false 13 | 14 | // # END:noIndex 15 | 16 | export { displayShipping }; 17 | -------------------------------------------------------------------------------- /arrays/push/push.spec.js: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | import { 3 | cart, 4 | reward, 5 | summarizeCart, 6 | summarizeCartUpdated, 7 | } from './push'; 8 | 9 | describe('push', () => { 10 | it('should not return an error intitially', () => { 11 | const testCart = [...cart]; 12 | const updated = [...cart, reward]; 13 | expect(summarizeCart(testCart)).toEqual({ 14 | discounts: 1, 15 | items: 4, 16 | cart: updated, 17 | }); 18 | }); 19 | 20 | it('should return an error when rearranged', () => { 21 | const testCart = [...cart]; 22 | expect(summarizeCartUpdated(testCart)).toEqual({ 23 | error: 'Can only have one discount', 24 | }); 25 | }); 26 | 27 | it('should not return an error with spread', () => { 28 | const testCart = [...cart]; 29 | const updated = [...cart, reward]; 30 | expect(summarizeCart(testCart)).toEqual({ 31 | discounts: 1, 32 | items: 4, 33 | cart: updated, 34 | }); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /arrays/sort/sort.js: -------------------------------------------------------------------------------- 1 | const staff = [ 2 | { 3 | name: 'Joe', 4 | years: 10, 5 | }, 6 | { 7 | name: 'Theo', 8 | years: 5, 9 | }, 10 | { 11 | name: 'Dyan', 12 | years: 10, 13 | }, 14 | ]; 15 | 16 | function sortByYears(a, b) { 17 | if (a.years === b.years) { 18 | return 0; 19 | } 20 | return a.years - b.years; 21 | } 22 | 23 | const sortByName = (a, b) => { 24 | if (a.name === b.name) { 25 | return 0; 26 | } 27 | return a.name > b.name ? 1 : -1; 28 | }; 29 | 30 | export { staff, sortByYears, sortByName }; 31 | 32 | -------------------------------------------------------------------------------- /arrays/sort/sort.spec.js: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | import { staff, sortByYears, sortByName } from './sort'; 3 | 4 | describe('sort mutations', () => { 5 | it('should return different results', () => { 6 | const copy = [...staff]; 7 | copy.sort(sortByYears); 8 | expect(copy[0].name).toEqual('Theo'); 9 | expect(copy[1].name).toEqual('Joe'); 10 | expect(copy[2].name).toEqual('Dyan'); 11 | 12 | copy.sort(sortByName); 13 | expect(copy[0].name).toEqual('Dyan'); 14 | expect(copy[1].name).toEqual('Joe'); 15 | expect(copy[2].name).toEqual('Theo'); 16 | 17 | copy.sort(sortByYears); 18 | expect(copy[0].name).toEqual('Theo'); 19 | expect(copy[1].name).toEqual('Dyan'); 20 | expect(copy[2].name).toEqual('Joe'); 21 | }); 22 | 23 | it('should return the same results', () => { 24 | const staff1 = [...staff].sort(sortByYears); 25 | expect(staff1[0].name).toEqual('Theo'); 26 | expect(staff1[1].name).toEqual('Joe'); 27 | expect(staff1[2].name).toEqual('Dyan'); 28 | 29 | const staff2 = [...staff].sort(sortByName); 30 | expect(staff2[0].name).toEqual('Dyan'); 31 | expect(staff2[1].name).toEqual('Joe'); 32 | expect(staff2[2].name).toEqual('Theo'); 33 | 34 | const staff3 = [...staff].sort(sortByYears); 35 | expect(staff3[0].name).toEqual('Theo'); 36 | expect(staff3[1].name).toEqual('Joe'); 37 | expect(staff3[2].name).toEqual('Dyan'); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /arrays/sort/sortMutate.js: -------------------------------------------------------------------------------- 1 | // # START:staff 2 | const staff = [ 3 | { 4 | name: 'Joe', 5 | years: 10, 6 | }, 7 | { 8 | name: 'Theo', 9 | years: 5, 10 | }, 11 | { 12 | name: 'Dyan', 13 | years: 10, 14 | }, 15 | ]; 16 | // # END:staff 17 | 18 | // # START:sortFunctions 19 | function sortByYears(a, b) { 20 | if (a.years === b.years) { 21 | return 0; 22 | } 23 | return a.years - b.years; 24 | } 25 | 26 | const sortByName = (a, b) => { 27 | if (a.name === b.name) { 28 | return 0; 29 | } 30 | return a.name > b.name ? 1 : -1; 31 | }; 32 | // # END:sortFunctions 33 | 34 | // # START:sortYears 35 | staff.sort(sortByYears); 36 | 37 | // [ 38 | // { 39 | // name: 'Theo', 40 | // years: 5 41 | // }, 42 | // { 43 | // name: 'Joe', 44 | // years: 10 45 | // }, 46 | // { 47 | // name: 'Dyan', 48 | // years: 10 49 | // }, 50 | // ]; 51 | // # END:sortYears 52 | 53 | // # START:sortName 54 | staff.sort(sortByName); 55 | 56 | // [ 57 | // { 58 | // name: 'Dyan', 59 | // years: 10 60 | // }, 61 | // { 62 | // name: 'Joe', 63 | // years: 10 64 | // }, 65 | // { 66 | // name: 'Theo', 67 | // years: 5 68 | // }, 69 | // ]; 70 | // # END:sortName 71 | 72 | // # START:sortYearsAgain 73 | staff.sort(sortByYears); 74 | 75 | // [ 76 | // { 77 | // name: 'Theo', 78 | // years: 5 79 | // }, 80 | // { 81 | // name: 'Dyan', 82 | // years: 10 83 | // }, 84 | // { 85 | // name: 'Joe', 86 | // years: 10 87 | // }, 88 | // ] 89 | // # END:sortYearsAgain 90 | -------------------------------------------------------------------------------- /arrays/sort/sortSpread.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | // # START:sortYears 3 | [...staff].sort(sortByYears); 4 | 5 | // [ 6 | // { 7 | // name: 'Theo', 8 | // years: 5 9 | // }, 10 | // { 11 | // name: 'Joe', 12 | // years: 10 13 | // }, 14 | // { 15 | // name: 'Dyan', 16 | // years: 10 17 | // }, 18 | // ]; 19 | 20 | // # END:sortYears 21 | -------------------------------------------------------------------------------- /arrays/spread/problem.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | // # START:removeItem 3 | function removeItem(items, removable) { 4 | const updated = []; 5 | for (let i = 0; i < items.length; i++) { 6 | if (items[i] !== removable) { 7 | updated.push(items[i]); 8 | } 9 | } 10 | return updated; 11 | } 12 | // # END:removeItem 13 | 14 | export { removeItem }; 15 | -------------------------------------------------------------------------------- /arrays/spread/slice.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | 3 | // # START:removeItemSlice 4 | function removeItem(items, removable) { 5 | if (items.includes(removable)) { 6 | const index = items.indexOf(removable); 7 | return items.slice(0, index).concat(items.slice(index + 1)); 8 | } 9 | return items; 10 | } 11 | // # END:removeItemSlice 12 | 13 | export { removeItem }; 14 | -------------------------------------------------------------------------------- /arrays/spread/splice.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | // # START:removeItemSplice 3 | function removeItem(items, removable) { 4 | if (items.includes(removable)) { 5 | const index = items.indexOf(removable); 6 | items.splice(index, 1); 7 | } 8 | return items; 9 | } 10 | // # END:removeItemSplice 11 | 12 | // # START:removeItemSpliceExample 13 | const books = ['practical vim', 'moby dick', 'the dark tower']; 14 | const recent = removeItem(books, 'moby dick'); 15 | const novels = removeItem(books, 'practical vim'); 16 | // # END:removeItemSpliceExample 17 | 18 | export { removeItem }; 19 | -------------------------------------------------------------------------------- /arrays/spread/spread.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | 3 | // # START:removeItemSpread 4 | function removeItem(items, removable) { 5 | if (items.includes(removable)) { 6 | const index = items.indexOf(removable); 7 | return [...items.slice(0, index), ...items.slice(index + 1)]; 8 | } 9 | return items; 10 | } 11 | // # END:removeItemSpread 12 | 13 | 14 | // # START:spreadArguments 15 | const book = ['Reasons and Persons', 'Derek Parfit', 19.99]; 16 | 17 | function formatBook(title, author, price) { 18 | return `${title} by ${author} $${price}`; 19 | } 20 | // # END:spreadArguments 21 | 22 | 23 | // # START:spreadArgumentsExample 24 | formatBook(book[0], book[1], book[2]); 25 | 26 | // # END:spreadArgumentsExample 27 | 28 | // # START:spreadArgumentsExample2 29 | formatBook(...book); 30 | // # END:spreadArgumentsExample2 31 | 32 | export { 33 | book, 34 | formatBook, 35 | removeItem, 36 | }; 37 | -------------------------------------------------------------------------------- /classes/bind/bind.js: -------------------------------------------------------------------------------- 1 | // START:bind 2 | function sayMessage() { 3 | return this.message; 4 | } 5 | 6 | const alert = { 7 | message: 'Danger!', 8 | }; 9 | 10 | const sayAlert = sayMessage.bind(alert); 11 | 12 | sayAlert(); 13 | // Danger! 14 | // END:bind 15 | 16 | // START:func 17 | class Validator { 18 | constructor() { 19 | this.message = 'is invalid.'; 20 | } 21 | 22 | setInvalidMessage(field) { 23 | return `${field} ${this.message}`; 24 | } 25 | 26 | setInvalidMessages(...fields) { 27 | return fields.map(this.setInvalidMessage.bind(this)); 28 | } 29 | } 30 | // END:func 31 | 32 | export { Validator, sayAlert, alert, sayMessage }; 33 | -------------------------------------------------------------------------------- /classes/bind/bind.spec.js: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | 3 | import { 4 | Validator as ValidatorProblem, 5 | } from './problem'; 6 | 7 | import { Validator } from './bind'; 8 | 9 | import { 10 | Validator as ValidatorArrow, 11 | } from './constructorArrow'; 12 | 13 | import { 14 | Validator as ValidatorConstructor, 15 | } from './constructor'; 16 | 17 | import { 18 | Validator as ValidatorProperties, 19 | } from './properties'; 20 | 21 | describe('Validator', () => { 22 | it('should not bind to this', () => { 23 | try { 24 | // START: problem 25 | const validator = new ValidatorProblem(); 26 | validator.setInvalidMessages('city'); 27 | // TypeError: Cannot read property 'message' of undefined 28 | // END: problem 29 | } catch (e) { 30 | expect(e.message).toEqual("Cannot read property 'message' of undefined"); 31 | } 32 | }); 33 | 34 | it('should bind methods in map function', () => { 35 | const v = new Validator(); 36 | const message = 'city is invalid.'; 37 | expect(v.setInvalidMessages('city')).toEqual([message]); 38 | }); 39 | 40 | it('should use arrow methods in constructor', () => { 41 | const v = new ValidatorArrow(); 42 | const message = 'city is invalid.'; 43 | expect(v.setInvalidMessages('city')).toEqual([message]); 44 | }); 45 | 46 | it('should bind methods in constructor', () => { 47 | const v = new ValidatorConstructor(); 48 | const message = 'city is invalid.'; 49 | expect(v.setInvalidMessages('city')).toEqual([message]); 50 | }); 51 | 52 | it('should have private properties', () => { 53 | const v = new ValidatorProperties(); 54 | expect(v.message).toEqual('is invalid.'); 55 | }); 56 | 57 | it('should bind this with arrow functions', () => { 58 | const v = new ValidatorProperties(); 59 | const message = 'city is invalid.'; 60 | expect(v.setInvalidMessages('city')).toEqual([message]); 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /classes/bind/constructor.js: -------------------------------------------------------------------------------- 1 | // START:func 2 | class Validator { 3 | constructor() { 4 | this.message = 'is invalid.'; 5 | this.setInvalidMessage = this.setInvalidMessage.bind(this); 6 | } 7 | 8 | setInvalidMessage(field) { 9 | return `${field} ${this.message}`; 10 | } 11 | 12 | setInvalidMessages(...fields) { 13 | return fields.map(this.setInvalidMessage); 14 | } 15 | } 16 | // END:func 17 | 18 | export { Validator }; 19 | -------------------------------------------------------------------------------- /classes/bind/constructorArrow.js: -------------------------------------------------------------------------------- 1 | // START:class 2 | class Validator { 3 | constructor() { 4 | this.message = 'is invalid.'; 5 | this.setInvalidMessage = field => `${field} ${this.message}`; 6 | } 7 | 8 | setInvalidMessages(...fields) { 9 | return fields.map(this.setInvalidMessage); 10 | } 11 | } 12 | // END:class 13 | 14 | export { Validator }; 15 | -------------------------------------------------------------------------------- /classes/bind/problem.js: -------------------------------------------------------------------------------- 1 | // START: class 2 | class Validator { 3 | constructor() { 4 | this.message = 'is invalid.'; 5 | } 6 | 7 | setInvalidMessage(field) { 8 | return `${field} ${this.message}`; 9 | } 10 | 11 | setInvalidMessages(...fields) { 12 | return fields.map(this.setInvalidMessage); 13 | } 14 | } 15 | // END: class 16 | 17 | export { Validator }; 18 | -------------------------------------------------------------------------------- /classes/bind/properties.js: -------------------------------------------------------------------------------- 1 | // START:func 2 | class Validator { 3 | message = 'is invalid.'; 4 | 5 | setMessage = field => `${field} ${this.message}`; 6 | 7 | setInvalidMessages(...fields) { 8 | return fields.map(this.setMessage); 9 | } 10 | } 11 | // END:func 12 | 13 | export { Validator }; 14 | -------------------------------------------------------------------------------- /classes/constructor/constructor.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable dot-notation */ 2 | 3 | // START:class 4 | class Coupon { 5 | constructor(price, expiration) { 6 | this.price = price; 7 | this.expiration = expiration || 'two weeks'; 8 | } 9 | } 10 | 11 | const coupon = new Coupon(5); 12 | coupon.price; 13 | // 5 14 | coupon['expiration']; 15 | // 'Two Weeks' 16 | // END:class 17 | 18 | export default Coupon; 19 | -------------------------------------------------------------------------------- /classes/constructor/constructor.spec.js: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | import CouponProblem from './problem'; 3 | import CouponSimple from './constructor'; 4 | import Coupon from './methods'; 5 | 6 | describe('basic class', () => { 7 | it('should make a class', () => { 8 | const coupon = new CouponProblem(); 9 | expect(coupon instanceof CouponProblem).toEqual(true); 10 | }); 11 | 12 | it('should set properties', () => { 13 | const coupon = new CouponSimple(5); 14 | expect(coupon.price).toEqual(5); 15 | expect(coupon.expiration).toEqual('two weeks'); 16 | }); 17 | 18 | it('should have methods', () => { 19 | const coupon = new Coupon(5); 20 | const message = 'This offer expires in two weeks.'; 21 | 22 | expect(coupon.getPriceText()).toEqual('$ 5'); 23 | expect(coupon.getExpirationMessage()).toEqual(message); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /classes/constructor/methods.js: -------------------------------------------------------------------------------- 1 | // START: method 2 | class Coupon { 3 | constructor(price, expiration) { 4 | this.price = price; 5 | this.expiration = expiration || 'two weeks'; 6 | } 7 | 8 | getPriceText() { 9 | return `$ ${this.price}`; 10 | } 11 | 12 | getExpirationMessage() { 13 | return `This offer expires in ${this.expiration}.`; 14 | } 15 | } 16 | 17 | const coupon = new Coupon(5); 18 | coupon.getPriceText(); 19 | // '$ 5' 20 | coupon.getExpirationMessage(); 21 | // 'This offer expires in two weeks.' 22 | 23 | // END: method 24 | 25 | export default Coupon; 26 | -------------------------------------------------------------------------------- /classes/constructor/problem.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | // START:class 3 | class Coupon { 4 | 5 | } 6 | 7 | const coupon = new Coupon(); 8 | // END:class 9 | 10 | export default Coupon; 11 | -------------------------------------------------------------------------------- /classes/extend/basic.js: -------------------------------------------------------------------------------- 1 | import Coupon from './extend'; 2 | 3 | class FlashCoupon extends Coupon { 4 | 5 | } 6 | 7 | const flash = new FlashCoupon(10); 8 | 9 | flash.price; 10 | // 10 11 | 12 | flash.getPriceText(); 13 | // "$ 10" 14 | -------------------------------------------------------------------------------- /classes/extend/constructor.js: -------------------------------------------------------------------------------- 1 | 2 | import Coupon from './extend'; 3 | 4 | class FlashCoupon extends Coupon { 5 | constructor(price, expiration) { 6 | super(price); 7 | this.expiration = expiration || 'two hours'; 8 | } 9 | } 10 | 11 | const flash = new FlashCoupon(10); 12 | 13 | flash.price; 14 | // 10 15 | 16 | flash.getExpirationMessage(); 17 | // "This offer expires in two hours" 18 | -------------------------------------------------------------------------------- /classes/extend/extend.js: -------------------------------------------------------------------------------- 1 | class Coupon { 2 | constructor(price, expiration) { 3 | this.price = price; 4 | this.expiration = expiration || 'Two Weeks'; 5 | } 6 | 7 | getPriceText() { 8 | return `$ ${this.price}`; 9 | } 10 | 11 | getExpirationMessage() { 12 | return `This offer expires in ${this.expiration}`; 13 | } 14 | 15 | isRewardsEligible(user) { 16 | return user.rewardsEligible && user.active; 17 | } 18 | 19 | getRewards(user) { 20 | if (this.isRewardsEligible(user)) { 21 | this.price = this.price * 0.9; 22 | } 23 | } 24 | } 25 | 26 | export default Coupon; 27 | -------------------------------------------------------------------------------- /classes/extend/extend.spec.js: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | import Coupon from './extend'; 3 | import { FlashCoupon } from './flash'; 4 | 5 | describe('coupon', () => { 6 | it('should have rewards for users', () => { 7 | const coupon = new Coupon(10); 8 | const user = { 9 | rewardsEligible: true, 10 | active: true, 11 | }; 12 | expect(coupon.price).toEqual(10); 13 | coupon.getRewards(user); 14 | expect(coupon.price).toEqual(9); 15 | }); 16 | 17 | it('should not reward ineligible users', () => { 18 | const coupon = new Coupon(10); 19 | const user = { 20 | rewardsEligible: true, 21 | active: false, 22 | }; 23 | expect(coupon.price).toEqual(10); 24 | coupon.getRewards(user); 25 | expect(coupon.price).toEqual(10); 26 | }); 27 | }); 28 | 29 | describe('flash coupon', () => { 30 | it('should call parent constructor', () => { 31 | const flash = new FlashCoupon(5); 32 | expect(flash.price).toEqual(5); 33 | }); 34 | 35 | it('should inherit methods', () => { 36 | const flash = new FlashCoupon(5); 37 | expect(flash.getPriceText()).toEqual('$ 5'); 38 | }); 39 | 40 | it('should override parent methods', () => { 41 | const flash = new FlashCoupon(5); 42 | const message = 'This is a flash offer and expires in two hours.'; 43 | expect(flash.getExpirationMessage()).toEqual(message); 44 | }); 45 | 46 | it('should call parent methods for user authentication', () => { 47 | const flash = new FlashCoupon(100); 48 | const user = { 49 | rewardsEligible: true, 50 | active: true, 51 | }; 52 | flash.getRewards(user); 53 | expect(flash.price).toEqual(80); 54 | }); 55 | 56 | it('should not give rewards to inactive user', () => { 57 | const flash = new FlashCoupon(100); 58 | const user = { 59 | rewardsEligible: true, 60 | active: false, 61 | }; 62 | flash.getRewards(user); 63 | expect(flash.price).toEqual(100); 64 | }); 65 | 66 | it('should not give rewards to user when price is too low', () => { 67 | const flash = new FlashCoupon(10); 68 | const user = { 69 | rewardsEligible: true, 70 | active: true, 71 | }; 72 | flash.getRewards(user); 73 | expect(flash.price).toEqual(10); 74 | }); 75 | }); 76 | -------------------------------------------------------------------------------- /classes/extend/flash.js: -------------------------------------------------------------------------------- 1 | import Coupon from './extend'; 2 | 3 | class FlashCoupon extends Coupon { 4 | constructor(price, expiration) { 5 | super(price); 6 | this.expiration = expiration || 'two hours'; 7 | } 8 | 9 | getExpirationMessage() { 10 | return `This is a flash offer and expires in ${this.expiration}.`; 11 | } 12 | 13 | isRewardsEligible(user) { 14 | return super.isRewardsEligible(user) && this.price > 20; 15 | } 16 | 17 | getRewards(user) { 18 | if (this.isRewardsEligible(user)) { 19 | this.price = this.price * 0.8; 20 | } 21 | } 22 | } 23 | 24 | export { FlashCoupon }; 25 | -------------------------------------------------------------------------------- /classes/extend/method.js: -------------------------------------------------------------------------------- 1 | import Coupon from './extend'; 2 | 3 | class FlashCoupon extends Coupon { 4 | constructor(price, expiration) { 5 | super(price); 6 | this.expiration = expiration || 'two hours'; 7 | } 8 | 9 | getExpirationMessage() { 10 | return `This is a flash offer and expires in ${this.expiration}.`; 11 | } 12 | } 13 | 14 | const flash = new FlashCoupon(10); 15 | 16 | flash.price; 17 | // 10 18 | 19 | flash.getExpirationMessage(); 20 | // "This is a flash offer and expires in two hours" 21 | -------------------------------------------------------------------------------- /classes/generators/generators.js: -------------------------------------------------------------------------------- 1 | // START:final 2 | class FamilyTree { 3 | constructor() { 4 | this.family = { 5 | name: 'Doris', 6 | child: { 7 | name: 'Martha', 8 | child: { 9 | name: 'Dyan', 10 | child: { 11 | name: 'Bea', 12 | }, 13 | }, 14 | }, 15 | }; 16 | } 17 | * [Symbol.iterator]() { 18 | let node = this.family; 19 | while (node) { 20 | yield node.name; 21 | node = node.child; 22 | } 23 | } 24 | } 25 | 26 | const family = new FamilyTree(); 27 | [...family]; 28 | // ['Doris', 'Martha', 'Dyan', 'Bea']; 29 | // END:final 30 | 31 | export default FamilyTree; 32 | -------------------------------------------------------------------------------- /classes/generators/generators.spec.js: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | import { getCairoTrilogy } from './simple'; 3 | import FamilyTree from './generators'; 4 | import FamilyTreeProblem from './problem'; 5 | 6 | describe('Cairo trilogy', () => { 7 | it('returns each book of the Cairo trilogy', () => { 8 | const trilogy = getCairoTrilogy(); 9 | expect(trilogy.next().value).toEqual('Palace Walk'); 10 | expect(trilogy.next().value).toEqual('Palace of Desire'); 11 | expect(trilogy.next().value).toEqual('Sugar Street'); 12 | expect(trilogy.next().done).toEqual(true); 13 | }); 14 | 15 | it('will spread the trilogy into an array', () => { 16 | const trilogy = [...getCairoTrilogy()]; 17 | expect(trilogy).toEqual([ 18 | 'Palace Walk', 19 | 'Palace of Desire', 20 | 'Sugar Street', 21 | ]); 22 | }); 23 | 24 | it('will add books to a reading list', () => { 25 | const readingList = { 26 | 'Visit from the Goon Squad': true, 27 | 'Manhattan Beach': false, 28 | }; 29 | for (const book of getCairoTrilogy()) { 30 | readingList[book] = false; 31 | } 32 | expect(readingList).toEqual({ 33 | 'Visit from the Goon Squad': true, 34 | 'Manhattan Beach': false, 35 | 'Palace Walk': false, 36 | 'Palace of Desire': false, 37 | 'Sugar Street': false, 38 | }); 39 | }); 40 | }); 41 | describe('should create an array of family members', () => { 42 | it('should get a list of family members with a method', () => { 43 | const family = new FamilyTreeProblem(); 44 | expect(family.getMembers()).toEqual(['Doris', 'Martha', 'Dyan', 'Bea']); 45 | }); 46 | it('should get a list of family members', () => { 47 | const family = new FamilyTree(); 48 | expect([...family]).toEqual(['Doris', 'Martha', 'Dyan', 'Bea']); 49 | }); 50 | }); 51 | -------------------------------------------------------------------------------- /classes/generators/problem.js: -------------------------------------------------------------------------------- 1 | class FamilyTree { 2 | constructor() { 3 | this.family = { 4 | name: 'Doris', 5 | child: { 6 | name: 'Martha', 7 | child: { 8 | name: 'Dyan', 9 | child: { 10 | name: 'Bea', 11 | }, 12 | }, 13 | }, 14 | }; 15 | } 16 | getMembers() { 17 | const family = []; 18 | let node = this.family; 19 | while (node) { 20 | family.push(node.name); 21 | node = node.child; 22 | } 23 | return family; 24 | } 25 | } 26 | 27 | const family = new FamilyTree(); 28 | family.getMembers(); 29 | // ['Doris', 'Martha', 'Dyan', 'Bea']; 30 | 31 | export default FamilyTree; 32 | -------------------------------------------------------------------------------- /classes/generators/simple.js: -------------------------------------------------------------------------------- 1 | // START: next 2 | function* getCairoTrilogy() { 3 | yield 'Palace Walk'; 4 | yield 'Palace of Desire'; 5 | yield 'Sugar Street'; 6 | } 7 | 8 | const trilogy = getCairoTrilogy(); 9 | trilogy.next(); 10 | // { value: 'Palace Walk', done: false } 11 | trilogy.next(); 12 | // { value: 'Palace of Desire', done: false } 13 | trilogy.next(); 14 | // { value: 'Sugar Street', done: false } 15 | trilogy.next(); 16 | // { value: undefined, done: true } 17 | // END: next 18 | 19 | // START: spread 20 | [...getCairoTrilogy]; 21 | // [ 'Palace Walk', 'Palace of Desire', 'Sugar Street'] 22 | // END: spread 23 | 24 | // START: loop 25 | const readingList = { 26 | 'Visit from the Goon Squad': true, 27 | 'Manhattan Beach': false, 28 | }; 29 | for (const book of getCairoTrilogy()) { 30 | readingList[book] = false; 31 | } 32 | readingList; 33 | // { 34 | // 'Visit from the Goon Squad': true, 35 | // 'Manhattan Beach': false, 36 | // 'Palace Walk': false, 37 | // 'Palace of Desire': false, 38 | // 'Sugar Street': false 39 | // } 40 | 41 | // END: loop 42 | 43 | export { getCairoTrilogy }; 44 | -------------------------------------------------------------------------------- /classes/get/get.js: -------------------------------------------------------------------------------- 1 | // START:solution 2 | class Coupon { 3 | constructor(price, expiration) { 4 | this._price = price; 5 | this.expiration = expiration || 'Two Weeks'; 6 | } 7 | 8 | get priceText() { 9 | return `$${this._price}`; 10 | } 11 | 12 | get price() { 13 | return this._price; 14 | } 15 | 16 | set price(price) { 17 | const newPrice = price 18 | .toString() 19 | .replace(/[^\d]/g, ''); 20 | this._price = newPrice; 21 | } 22 | 23 | get expirationMessage() { 24 | return `This offer expires in ${this.expiration}`; 25 | } 26 | } 27 | 28 | const coupon = new Coupon(5); 29 | coupon.price; 30 | // 5 31 | 32 | coupon.price = '$10'; 33 | 34 | coupon.price; 35 | // 10 36 | 37 | coupon.priceText; 38 | // $ 10 39 | // END:solution 40 | export default Coupon; 41 | -------------------------------------------------------------------------------- /classes/get/get.spec.js: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | 3 | import Coupon from './get'; 4 | import CouponProblem from './problem'; 5 | import CouponGetters from './price'; 6 | import CouponSet from './set'; 7 | 8 | describe('get problem', () => { 9 | it('should set price as a string', () => { 10 | const coupon = new CouponProblem(5); 11 | coupon.price = '$10'; 12 | expect(coupon.getPriceText()).toEqual('$ $10'); 13 | }); 14 | }); 15 | 16 | describe('Getters', () => { 17 | const coupon = new CouponGetters(10); 18 | it('should get price text', () => { 19 | expect(coupon.priceText).toEqual('$ 10'); 20 | }); 21 | 22 | it('should get expiration text', () => { 23 | const message = 'This offer expires in two weeks.'; 24 | expect(coupon.expirationMessage).toEqual(message); 25 | }); 26 | }); 27 | 28 | describe('Setters', () => { 29 | const coupon = new CouponSet(10); 30 | it('should get price', () => { 31 | expect(coupon.price).toEqual(10); 32 | }); 33 | 34 | it('should set halfPrice', () => { 35 | coupon.halfPrice = 40; 36 | expect(coupon.price).toEqual(20); 37 | }); 38 | }); 39 | 40 | describe('getters and setters', () => { 41 | it('should get a inner variable', () => { 42 | const coupon = new Coupon(10); 43 | expect(coupon.price).toEqual(coupon._price); 44 | }); 45 | 46 | it('should set inner property', () => { 47 | const coupon = new Coupon(10); 48 | expect(coupon.price).toEqual(10); 49 | coupon.price = 100; 50 | expect(coupon.price).toEqual(100); 51 | }); 52 | 53 | it('should parse int when setting', () => { 54 | const coupon = new Coupon(10); 55 | expect(coupon.price).toEqual(10); 56 | coupon.price = '$100'; 57 | expect(coupon.price).toEqual(100); 58 | }); 59 | 60 | it('should get a message', () => { 61 | const coupon = new Coupon(10); 62 | expect(coupon.priceText).toEqual('$10'); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /classes/get/invalid.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | // START:coupon 3 | class Coupon { 4 | constructor(price, expiration) { 5 | this.price = price; 6 | this.expiration = expiration || 'Two Weeks'; 7 | } 8 | 9 | get price() { 10 | return this.price; 11 | } 12 | 13 | set price(price) { 14 | this.price = `$ ${price}`; 15 | } 16 | } 17 | 18 | const coupon = new Coupon(5); 19 | // RangeError: Maximum call stack size exceeded 20 | // END:coupon 21 | -------------------------------------------------------------------------------- /classes/get/price.js: -------------------------------------------------------------------------------- 1 | // START:getter 2 | class Coupon { 3 | constructor(price, expiration) { 4 | this.price = price; 5 | this.expiration = expiration || 'two weeks'; 6 | } 7 | 8 | get priceText() { 9 | return `$ ${this.price}`; 10 | } 11 | 12 | get expirationMessage() { 13 | return `This offer expires in ${this.expiration}.`; 14 | } 15 | } 16 | // END:getter 17 | 18 | // START:use 19 | const coupon = new Coupon(5); 20 | coupon.price = 10; 21 | coupon.priceText; 22 | // '$10' 23 | coupon.messageText; 24 | // 'This offer expires in two weeks. 25 | coupon.messageText; 26 | // END:use 27 | 28 | export default Coupon; 29 | -------------------------------------------------------------------------------- /classes/get/problem.js: -------------------------------------------------------------------------------- 1 | // START:problem 2 | class Coupon { 3 | constructor(price, expiration) { 4 | this.price = price; 5 | this.expiration = expiration || 'Two Weeks'; 6 | } 7 | 8 | getPriceText() { 9 | return `$ ${this.price}`; 10 | } 11 | 12 | getExpirationMessage() { 13 | return `This offer expires in ${this.expiration}`; 14 | } 15 | } 16 | 17 | const coupon = new Coupon(5); 18 | coupon.price = '$10'; 19 | coupon.getPriceText(); 20 | // '$ $10' 21 | // END:problem 22 | 23 | export default Coupon; 24 | -------------------------------------------------------------------------------- /classes/get/set.js: -------------------------------------------------------------------------------- 1 | // START:set 2 | class Coupon { 3 | constructor(price, expiration) { 4 | this.price = price; 5 | this.expiration = expiration || 'Two Weeks'; 6 | } 7 | 8 | set halfPrice(price) { 9 | this.price = price / 2; 10 | } 11 | } 12 | // END:set 13 | 14 | // START:use 15 | const coupon = new Coupon(5); 16 | coupon.price; 17 | // 5 18 | coupon.halfPrice = 20; 19 | coupon.price; 20 | // 10 21 | coupon.halfPrice; 22 | // undefined 23 | // END:use 24 | 25 | export default Coupon; 26 | -------------------------------------------------------------------------------- /classes/prototypes/class.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable func-names */ 2 | // START:class 3 | class Coupon { 4 | constructor(price, expiration) { 5 | this.price = price; 6 | this.expiration = expiration || 'Two Weeks'; 7 | } 8 | 9 | getExpirationMessage() { 10 | return `This offer expires in ${this.expiration}.`; 11 | } 12 | } 13 | 14 | const saleCoupon = new Coupon(5, 'two months'); 15 | saleCoupon.getExpirationMessage(); 16 | // This offer expires in two months. 17 | 18 | // END:class 19 | 20 | class FlashCoupon extends Coupon { 21 | constructor(...args) { 22 | super(args); 23 | this.expiration = 'Two Hours'; 24 | } 25 | 26 | getExpirationMessage() { 27 | return `This is a flash offer and expires in ${this.expiration}`; 28 | } 29 | } 30 | 31 | export { Coupon, FlashCoupon }; 32 | -------------------------------------------------------------------------------- /classes/prototypes/prototypes.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable func-names */ 2 | // START:instance 3 | function Coupon(price, expiration) { 4 | this.price = price; 5 | this.expiration = expiration || 'two weeks'; 6 | } 7 | const coupon = new Coupon(5, 'two months'); 8 | coupon.price; 9 | // 5 10 | // END:instance 11 | 12 | // START:prototype 13 | Coupon.prototype.getExpirationMessage = function () { 14 | return `This offer expires in ${this.expiration}.`; 15 | }; 16 | coupon.getExpirationMessage(); 17 | // This offer expires in two months. 18 | 19 | // END:prototype 20 | 21 | // START:extend 22 | class FlashCoupon extends Coupon { 23 | constructor(price, expiration) { 24 | super(price); 25 | this.expiration = expiration || 'two hours'; 26 | } 27 | 28 | getExpirationMessage() { 29 | return `This is a flash offer and expires in ${this.expiration}.`; 30 | } 31 | } 32 | // END:extend 33 | 34 | export { Coupon, FlashCoupon }; 35 | -------------------------------------------------------------------------------- /classes/prototypes/prototypes.spec.js: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | 3 | import { Coupon, FlashCoupon } from './prototypes'; 4 | 5 | describe('prototypes', () => { 6 | it('should have methods on a prototype', () => { 7 | const coupon = new Coupon(5, 'two months'); 8 | const message = 'This offer expires in two months.'; 9 | expect(coupon.price).toEqual(5); 10 | expect(coupon.getExpirationMessage()).toEqual(message); 11 | }); 12 | 13 | it('should extend a prototype', () => { 14 | const flash = new FlashCoupon(5); 15 | const message = 'This is a flash offer and expires in two hours.'; 16 | expect(flash.price).toEqual(5); 17 | expect(flash.getExpirationMessage()).toEqual(message); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /collections/assign/assign.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | 3 | // # START:pure 4 | const defaults = { author: '', 5 | title: '', 6 | year: 2017, 7 | rating: null, 8 | }; 9 | 10 | const book = { 11 | author: 'Joe Morgan', 12 | title: 'Simplifying JavaScript', 13 | }; 14 | 15 | const updated = Object.assign({}, defaults, book); 16 | // # END:pure 17 | 18 | // # START:deep 19 | const defaultEmployee = { 20 | name: { 21 | first: '', 22 | last: '', 23 | }, 24 | years: 0, 25 | }; 26 | 27 | const employee = Object.assign({}, defaultEmployee); 28 | // # END:deep 29 | 30 | // # START:deepMutate 31 | employee.name.first = 'Joe'; 32 | 33 | defaultEmployee; 34 | 35 | // { 36 | // name: { 37 | // first:'Joe', 38 | // last: '', 39 | // }, 40 | // years: 0 41 | // } 42 | // # END:deepMutate 43 | 44 | 45 | // # START:deepMerge 46 | const employee2 = Object.assign( 47 | {}, 48 | defaultEmployee, 49 | { 50 | name: Object.assign({}, defaultEmployee.name), 51 | }, 52 | ); 53 | // # END:endMerge 54 | 55 | export { defaults }; 56 | -------------------------------------------------------------------------------- /collections/assign/assign.spec.js: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | import { addBookDefaults } from './problem'; 3 | 4 | describe('Assign tests', () => { 5 | const defaults = { 6 | author: '', 7 | title: '', 8 | year: 2017, 9 | rating: null, 10 | }; 11 | 12 | const book = { 13 | title: 'Simplifying JavaScript', 14 | author: 'Joe Morgan', 15 | }; 16 | 17 | it('should add defaults', () => { 18 | const bookUpdated = addBookDefaults(book, defaults); 19 | expect(bookUpdated.title).toEqual('Simplifying JavaScript'); 20 | expect(bookUpdated.year).toEqual(2017); 21 | }); 22 | 23 | it('should add defaults with assign', () => { 24 | const copy = Object.assign({}, defaults); 25 | const book2 = { 26 | title: 'Another book', 27 | year: 2016, 28 | }; 29 | const bookUpdated = Object.assign(copy, book); 30 | expect(bookUpdated.title).toEqual('Simplifying JavaScript'); 31 | expect(bookUpdated.author).toEqual('Joe Morgan'); 32 | expect(bookUpdated.year).toEqual(2017); 33 | const bookUpdated2 = Object.assign(copy, book2); 34 | expect(bookUpdated2.title).toEqual('Another book'); 35 | expect(bookUpdated2.author).toEqual('Joe Morgan'); 36 | expect(bookUpdated2.year).toEqual(2016); 37 | }); 38 | 39 | it('should add defaults with assign', () => { 40 | const bookUpdated = Object.assign({}, defaults, book); 41 | expect(bookUpdated.title).toEqual('Simplifying JavaScript'); 42 | expect(bookUpdated.year).toEqual(2017); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /collections/assign/mutate.js: -------------------------------------------------------------------------------- 1 | 2 | const book = { 3 | author: 'Joe Morgan', 4 | title: 'Simplifying JavaScript', 5 | }; 6 | 7 | const defaults = { 8 | author: '', 9 | title: '', 10 | year: 2017, 11 | rating: null, 12 | }; 13 | 14 | // # START:mutate 15 | Object.assign(defaults, book); 16 | 17 | // { 18 | // author: 'Joe Morgan', 19 | // title: 'Simplifying JavaScript', 20 | // year: 2017, 21 | // rating: null, 22 | // } 23 | // # END:mutate 24 | 25 | // # START:mutate2 26 | const anotherBook = { 27 | title: 'Another book', 28 | year: 2016, 29 | }; 30 | 31 | Object.assign(defaults, anotherBook); 32 | 33 | // { 34 | // author: 'Joe Morgan', 35 | // title: 'Another book', 36 | // year: 2016, 37 | // rating: null, 38 | // } 39 | // # END:mutate2 40 | -------------------------------------------------------------------------------- /collections/assign/problem.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | 3 | const original = { 4 | author: '', 5 | title: '', 6 | year: 2017, 7 | rating: null, 8 | }; 9 | // # START:add 10 | const defaults = { 11 | author: '', 12 | title: '', 13 | year: 2017, 14 | rating: null, 15 | }; 16 | 17 | const book = { 18 | author: 'Joe Morgan', 19 | title: 'Simplifying JavaScript', 20 | }; 21 | 22 | function addBookDefaults(book, defaults) { 23 | const fields = Object.keys(defaults); 24 | const updated = {}; 25 | for (let i = 0; i < fields.length; i++) { 26 | const field = fields[i]; 27 | updated[field] = book[field] || defaults[field]; 28 | } 29 | return updated; 30 | } 31 | // # END:add 32 | 33 | export { addBookDefaults, defaults }; 34 | -------------------------------------------------------------------------------- /collections/map/map.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-param-reassign, no-unused-vars */ 2 | 3 | // # START:mapActions 4 | const petFilters = new Map(); 5 | function addFilters(filters, key, value) { 6 | filters.set(key, value); 7 | } 8 | 9 | function deleteFilters(filters, key) { 10 | filters.delete(key); 11 | } 12 | 13 | function clearFilters(filters) { 14 | filters.clear(); 15 | } 16 | // # END:mapActions 17 | 18 | export { 19 | addFilters, 20 | deleteFilters, 21 | clearFilters, 22 | }; 23 | -------------------------------------------------------------------------------- /collections/map/map.spec.js: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | 3 | import { 4 | addFilters, 5 | clearFilters, 6 | deleteFilters, 7 | } from './map'; 8 | 9 | import { 10 | addFilters as addFiltersObject, 11 | clearFilters as clearFiltersObject, 12 | deleteFilters as deleteFiltersObject, 13 | } from './problem'; 14 | 15 | describe('Map tests', () => { 16 | it('should set value on object', () => { 17 | const filters = { 18 | color: 'brown', 19 | }; 20 | addFiltersObject(filters, 'size', 'large'); 21 | expect(filters.size).toEqual('large'); 22 | }); 23 | 24 | it('should delete the value on object', () => { 25 | const filters = { 26 | color: 'brown', 27 | size: 'large', 28 | }; 29 | deleteFiltersObject(filters, 'color'); 30 | expect(filters).toEqual({ size: 'large' }); 31 | }); 32 | 33 | it('should clear the object', () => { 34 | let filters = { 35 | color: 'brown', 36 | size: 'large', 37 | }; 38 | filters = clearFiltersObject(filters); 39 | expect(filters).toEqual({}); 40 | }); 41 | 42 | it('should set value on object', () => { 43 | const filters = new Map().set('color', 'brown'); 44 | addFilters(filters, 'size', 'large'); 45 | expect(filters.get('size')).toEqual('large'); 46 | }); 47 | 48 | it('should delete the value on object', () => { 49 | const filters = new Map() 50 | .set('color', 'brown') 51 | .set('size', 'large'); 52 | deleteFilters(filters, 'color'); 53 | expect([...filters.keys()]).toEqual(['size']); 54 | }); 55 | 56 | it('should clear the object', () => { 57 | const filters = new Map() 58 | .set('color', 'brown') 59 | .set('size', 'large'); 60 | clearFilters(filters); 61 | expect([...filters.keys()]).toEqual([]); 62 | }); 63 | }); 64 | -------------------------------------------------------------------------------- /collections/map/problem.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-param-reassign, no-unused-vars */ 2 | 3 | // # START:objectActions 4 | function addFilters(filters, key, value) { 5 | filters[key] = value; 6 | } 7 | 8 | function deleteFilters(filters, key) { 9 | delete filters[key]; 10 | } 11 | 12 | function clearFilters(filters) { 13 | filters = {}; 14 | return filters; 15 | } 16 | // # END:objectActions 17 | 18 | export { 19 | addFilters, 20 | deleteFilters, 21 | clearFilters, 22 | }; 23 | -------------------------------------------------------------------------------- /collections/mapSideEffects/copy.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-restricted-syntax */ 2 | 3 | // # START:apply 4 | function applyDefaults(map, defaults) { 5 | const copy = new Map([...map]); 6 | for (const [key, value] of defaults) { 7 | if (!copy.has(key)) { 8 | copy.set(key, value); 9 | } 10 | } 11 | return copy; 12 | } 13 | // # END:apply 14 | 15 | export { applyDefaults }; 16 | -------------------------------------------------------------------------------- /collections/mapSideEffects/map.js: -------------------------------------------------------------------------------- 1 | // START:apply 2 | function applyDefaults(map, defaults) { 3 | return new Map([...defaults, ...map]); 4 | } 5 | // END:apply 6 | 7 | export { applyDefaults }; 8 | -------------------------------------------------------------------------------- /collections/mapSideEffects/map.spec.js: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | 3 | import { applyDefaults } from './map'; 4 | 5 | import { applyDefaults as applySideEffects } from './sideEffects'; 6 | 7 | import { applyDefaults as applyCopy } from './copy'; 8 | 9 | describe('apply defaults to a map', () => { 10 | it('should mutate the original', () => { 11 | const defaults = new Map() 12 | .set('color', 'brown') 13 | .set('breed', 'beagle') 14 | .set('state', 'kansas'); 15 | 16 | 17 | const filters = new Map() 18 | .set('color', 'black'); 19 | 20 | applySideEffects(filters, defaults); 21 | 22 | expect(filters.get('color')).toEqual('black'); 23 | expect(filters.get('state')).toEqual('kansas'); 24 | }); 25 | 26 | it('should set new key-value', () => { 27 | const defaults = new Map() 28 | .set('color', 'brown') 29 | .set('breed', 'beagle') 30 | .set('state', 'kansas'); 31 | 32 | 33 | const filters = new Map() 34 | .set('color', 'black'); 35 | 36 | const update = applyCopy(filters, defaults); 37 | expect(update.get('color')).toEqual('black'); 38 | expect(update.get('state')).toEqual('kansas'); 39 | }); 40 | 41 | it('should set new key-value', () => { 42 | const defaults = new Map() 43 | .set('color', 'brown') 44 | .set('breed', 'beagle') 45 | .set('state', 'kansas'); 46 | 47 | 48 | const filters = new Map() 49 | .set('color', 'black'); 50 | 51 | const update = applyDefaults(filters, defaults); 52 | expect(update.get('color')).toEqual('black'); 53 | expect(update.get('state')).toEqual('kansas'); 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /collections/mapSideEffects/sideEffects.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-restricted-syntax, no-unused-vars */ 2 | 3 | // # START:maps 4 | const defaults = new Map() 5 | .set('color', 'brown') 6 | .set('breed', 'beagle') 7 | .set('state', 'kansas'); 8 | 9 | 10 | const filters = new Map() 11 | .set('color', 'black'); 12 | 13 | // # END:maps 14 | 15 | // # START:apply 16 | function applyDefaults(map, defaults) { 17 | for (const [key, value] of defaults) { 18 | if (!map.has(key)) { 19 | map.set(key, value); 20 | } 21 | } 22 | } 23 | // # START:apply 24 | 25 | export { applyDefaults }; 26 | -------------------------------------------------------------------------------- /collections/mapSpread/iterate.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console, no-unused-vars, no-restricted-syntax */ 2 | 3 | // START:iterate 4 | const filters = new Map() 5 | .set('color', 'black') 6 | .set('breed', 'labrador'); 7 | 8 | function checkFilters(filters) { 9 | for (const entry of filters) { 10 | console.log(entry); 11 | } 12 | } 13 | // ['color', 'black'] 14 | // ['breed', 'labrador'] 15 | // END:iterate 16 | 17 | // START:iterateGet 18 | function getAppliedFilters(filters) { 19 | const applied = []; 20 | for (const [key, value] of filters) { 21 | applied.push(`${key}:${value}`); 22 | } 23 | return `Your filters are: ${applied.join(', ')}.`; 24 | } 25 | 26 | // 'Your filters are: color:black, breed:labrador.' 27 | // END:iterateGet 28 | 29 | // START:iterateSort 30 | function sortByKey(a, b) { 31 | return a[0] > b[0] ? 1 : -1; 32 | } 33 | 34 | function getSortedAppliedFilters(filters) { 35 | const applied = []; 36 | for (const [key, value] of [...filters].sort(sortByKey)) { 37 | applied.push(`${key}:${value}`); 38 | } 39 | return `Your filters are: ${applied.join(', ')}.`; 40 | } 41 | // 'Your filters are: breed:labrador, color:black.' 42 | // END:iterateSort 43 | 44 | export { getAppliedFilters, getSortedAppliedFilters }; 45 | -------------------------------------------------------------------------------- /collections/mapSpread/mapSpread.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | 3 | const filters = new Map() 4 | .set('color', 'black') 5 | .set('breed', 'labrador'); 6 | 7 | // START:get 8 | function getAppliedFilters(filters) { 9 | const applied = [...filters].map(([key, value]) => { 10 | return `${key}:${value}`; 11 | }); 12 | return `Your filters are: ${applied.join(', ')}.`; 13 | } 14 | 15 | // 'Your filters are: color:black, breed:labrador.' 16 | // END:get 17 | 18 | function sortByKey(a, b) { 19 | return a[0] > b[0] ? 1 : -1; 20 | } 21 | 22 | // START:sort 23 | function getSortedAppliedFilters(filters) { 24 | const applied = [...filters] 25 | .sort(sortByKey) 26 | .map(([key, value]) => { 27 | return `${key}:${value}`; 28 | }) 29 | .join(', '); 30 | 31 | return `Your filters are: ${applied}.`; 32 | } 33 | // 'Your filters are: breed:labrador, color:black.' 34 | // END:sort 35 | 36 | export { getAppliedFilters, getSortedAppliedFilters }; 37 | -------------------------------------------------------------------------------- /collections/mapSpread/mapSpread.spec.js: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | 3 | import { 4 | getAppliedFilters as getObject, 5 | getSortedAppliedFilters as getSortObject, 6 | } from './object'; 7 | 8 | import { 9 | getAppliedFilters as getIterate, 10 | getSortedAppliedFilters as getSortIterate, 11 | } from './iterate'; 12 | 13 | import { 14 | getAppliedFilters, 15 | getSortedAppliedFilters, 16 | } from './mapSpread'; 17 | 18 | describe('object get', () => { 19 | it('should get key-values from object', () => { 20 | const filters = { 21 | color: 'black', 22 | breed: 'labrador', 23 | }; 24 | expect(getObject(filters)).toEqual('Your filters are: color:black, breed:labrador.'); 25 | }); 26 | 27 | it('should get sorted key-values from object', () => { 28 | const filters = { 29 | color: 'black', 30 | breed: 'labrador', 31 | }; 32 | expect(getSortObject(filters)).toEqual('Your filters are: breed:labrador, color:black.'); 33 | }); 34 | }); 35 | 36 | describe('map iterate', () => { 37 | it('should get key-values from object', () => { 38 | const filters = new Map() 39 | .set('color', 'black') 40 | .set('breed', 'labrador'); 41 | 42 | expect(getIterate(filters)).toEqual('Your filters are: color:black, breed:labrador.'); 43 | }); 44 | 45 | it('should get sorted key-values from object', () => { 46 | const filters = new Map() 47 | .set('color', 'black') 48 | .set('breed', 'labrador'); 49 | 50 | expect(getSortIterate(filters)).toEqual('Your filters are: breed:labrador, color:black.'); 51 | }); 52 | }); 53 | 54 | describe('map spread', () => { 55 | it('should get key-values from object', () => { 56 | const filters = new Map() 57 | .set('color', 'black') 58 | .set('breed', 'labrador'); 59 | 60 | expect(getAppliedFilters(filters)).toEqual('Your filters are: color:black, breed:labrador.'); 61 | }); 62 | 63 | it('should get sorted key-values from object', () => { 64 | const filters = new Map() 65 | .set('color', 'black') 66 | .set('breed', 'labrador'); 67 | 68 | expect(getSortedAppliedFilters(filters)).toEqual('Your filters are: breed:labrador, color:black.'); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /collections/mapSpread/object.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars, no-restricted-syntax */ 2 | 3 | // # START:objectIterate 4 | const filters = { 5 | color: 'black', 6 | breed: 'labrador', 7 | }; 8 | 9 | function getAppliedFilters(filters) { 10 | const keys = Object.keys(filters); 11 | const applied = []; 12 | for (const key of keys) { 13 | applied.push(`${key}:${filters[key]}`); 14 | } 15 | return `Your filters are: ${applied.join(', ')}.`; 16 | } 17 | 18 | // 'Your filters are: color:black, breed:labrador.' 19 | // # END:objectIterate 20 | 21 | 22 | // # START:objectSortIterate 23 | function getSortedAppliedFilters(filters) { 24 | const keys = Object.keys(filters); 25 | keys.sort(); 26 | const applied = []; 27 | for (const key of keys) { 28 | applied.push(`${key}:${filters[key]}`); 29 | } 30 | return `Your filters are: ${applied.join(', ')}.`; 31 | } 32 | // 'Your filters are: breed:labrador, color:black.' 33 | // # END:objectSortIterate 34 | 35 | export { 36 | getAppliedFilters, 37 | getSortedAppliedFilters, 38 | }; 39 | -------------------------------------------------------------------------------- /collections/object/object.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef, no-unused-vars */ 2 | 3 | // # START:book 4 | const book = { 5 | title: 'ES6 Tips', 6 | author: 'Joe Morgan', 7 | date: 2017, 8 | }; 9 | // # END:book 10 | 11 | // # START:config 12 | export const config = { 13 | endpoint: 'http://pragprog.com', 14 | key: 'secretkey', 15 | }; 16 | // # END:config 17 | 18 | // # START:between 19 | function getBill(item) { 20 | return { 21 | name: item.name, 22 | due: twoWeeksFromNow(), 23 | total: calculateTotal(item.price), 24 | }; 25 | } 26 | 27 | const bill = getBill({ name: 'Room Cleaning', price: 30 }); 28 | 29 | function displayBill(bill) { 30 | return `Your total ${bill.total} for ${bill.name} is due on ${bill.due}`; 31 | } 32 | // # END:between 33 | -------------------------------------------------------------------------------- /collections/objectSpread/objectSpread.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | 3 | function updateBookYear() { 4 | // # START:year 5 | const book = { 6 | title: 'Reasons and Persons', 7 | author: 'Derek Parfit', 8 | }; 9 | 10 | const update = { ...book, year: 1984 }; 11 | 12 | // { title: 'Reasons and Persons', author: 'Derek Parfit', year: 1984} 13 | // # END:year 14 | return update; 15 | } 16 | 17 | function updateBookTitle() { 18 | // # START:title 19 | const book = { 20 | title: 'Reasons and Persons', 21 | author: 'Derek Parfit', 22 | }; 23 | 24 | const update = { ...book, title: 'Reasons & Persons' }; 25 | 26 | // { title: 'Reasons & Persons', author: 'Derek Parfit' } 27 | // # END:title 28 | return update; 29 | } 30 | 31 | function addBookDefaults() { 32 | // # START:add 33 | const defaults = { 34 | author: '', 35 | title: '', 36 | year: 2017, 37 | rating: null, 38 | }; 39 | 40 | const book = { 41 | author: 'Joe Morgan', 42 | title: 'ES6 Tips', 43 | }; 44 | 45 | 46 | const bookWithDefaults = { ...defaults, ...book }; 47 | 48 | // { 49 | // author: 'Joe Morgan', 50 | // title: 'ES6 Tips', 51 | // year: 2017, 52 | // rating: null, 53 | // } 54 | // # END:add 55 | 56 | return bookWithDefaults; 57 | } 58 | 59 | function deepMerge() { 60 | const defaultEmployee = { 61 | name: { 62 | first: '', 63 | last: '', 64 | }, 65 | years: 0, 66 | }; 67 | // # START:deepMerge 68 | const employee = { 69 | ...defaultEmployee, 70 | name: { 71 | ...defaultEmployee.name, 72 | }, 73 | }; 74 | // # END:deepMerge 75 | 76 | employee.name.first = 'joe'; 77 | return [defaultEmployee.name.first, employee.name.first]; 78 | } 79 | export { addBookDefaults, deepMerge, updateBookTitle, updateBookYear }; 80 | -------------------------------------------------------------------------------- /collections/objectSpread/objectSpread.spec.js: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | import { addBookDefaults, deepMerge, updateBookYear, updateBookTitle } from './objectSpread'; 3 | 4 | describe('Spread tests', () => { 5 | it('should update book year', () => { 6 | const updated = { 7 | title: 'Reasons and Persons', 8 | author: 'Derek Parfit', 9 | year: 1984, 10 | }; 11 | 12 | expect(updateBookYear()).toEqual(updated); 13 | }); 14 | 15 | it('should update book author', () => { 16 | const updated = { 17 | title: 'Reasons & Persons', 18 | author: 'Derek Parfit', 19 | }; 20 | expect(updateBookTitle()).toEqual(updated); 21 | }); 22 | 23 | it('should add defaults', () => { 24 | const bookUpdated = addBookDefaults(); 25 | expect(bookUpdated.title).toEqual('ES6 Tips'); 26 | expect(bookUpdated.year).toEqual(2017); 27 | }); 28 | 29 | it('should deep merge', () => { 30 | const [name1, name2] = deepMerge(); 31 | expect(name1).toNotEqual(name2); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /collections/set/set.js: -------------------------------------------------------------------------------- 1 | // #START:unique 2 | function getUnique(attributes) { 3 | return [...new Set(attributes)]; 4 | } 5 | // #END:unique 6 | 7 | // #START:uniqueColors 8 | function getUniqueColors(dogs) { 9 | const unique = new Set(); 10 | for (const dog of dogs) { 11 | unique.add(dog.color); 12 | } 13 | return [...unique]; 14 | } 15 | // #END:uniqueColors 16 | 17 | function getUniqueColorsReduce(dogs) { 18 | const colors = 19 | // #START:uniqueReduce 20 | [...dogs.reduce((colors, { color }) => colors.add(color), new Set())]; 21 | // #END:uniqueReduce 22 | return colors; 23 | } 24 | 25 | export { getUnique, getUniqueColors, getUniqueColorsReduce }; 26 | -------------------------------------------------------------------------------- /collections/set/set.spec.js: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | 3 | import { 4 | getUnique as uniqueLoop, 5 | } from './unique'; 6 | 7 | import { 8 | getUnique, 9 | getUniqueColors, 10 | getUniqueColorsReduce, 11 | } from './set'; 12 | 13 | describe('set', () => { 14 | it('should get unique with a loop', () => { 15 | const colors = ['black', 'black', 'chocolate', 'yellow']; 16 | expect(uniqueLoop(colors)).toEqual(['black', 'chocolate', 'yellow']); 17 | }); 18 | 19 | it('should get unique with Set', () => { 20 | const colors = ['black', 'black', 'chocolate', 'yellow']; 21 | expect(getUnique(colors)).toEqual(['black', 'chocolate', 'yellow']); 22 | }); 23 | 24 | it('should get unique colors Set', () => { 25 | const dogs = [ 26 | { 27 | name: 'max', 28 | size: 'small', 29 | breed: 'boston terrier', 30 | color: 'black', 31 | }, 32 | { 33 | name: 'don', 34 | size: 'large', 35 | breed: 'labrador', 36 | color: 'black', 37 | }, 38 | { 39 | name: 'shadow', 40 | size: 'medium', 41 | breed: 'labrador', 42 | color: 'chocolate', 43 | }, 44 | ]; 45 | expect(getUniqueColors(dogs)).toEqual(['black', 'chocolate']); 46 | }); 47 | 48 | it('should get unique colors with Set and reducer', () => { 49 | const dogs = [ 50 | { 51 | name: 'max', 52 | size: 'small', 53 | breed: 'boston terrier', 54 | color: 'black', 55 | }, 56 | { 57 | name: 'don', 58 | size: 'large', 59 | breed: 'labrador', 60 | color: 'black', 61 | }, 62 | { 63 | name: 'shadow', 64 | size: 'medium', 65 | breed: 'labrador', 66 | color: 'chocolate', 67 | }, 68 | ]; 69 | expect(getUniqueColorsReduce(dogs)).toEqual(['black', 'chocolate']); 70 | }); 71 | }); 72 | -------------------------------------------------------------------------------- /collections/set/unique.js: -------------------------------------------------------------------------------- 1 | // #START:dog 2 | const dogs = [ 3 | { 4 | name: 'max', 5 | size: 'small', 6 | breed: 'boston terrier', 7 | color: 'black', 8 | }, 9 | { 10 | name: 'don', 11 | size: 'large', 12 | breed: 'labrador', 13 | color: 'black', 14 | }, 15 | { 16 | name: 'shadow', 17 | size: 'medium', 18 | breed: 'labrador', 19 | color: 'chocolate', 20 | }, 21 | ]; 22 | // #END:dogs 23 | // #START:colors 24 | function getColors(dogs) { 25 | return dogs.map(dog => dog.color); 26 | } 27 | 28 | getColors(dogs); 29 | 30 | // ['black', 'black', 'chocolate'] 31 | 32 | // #END:colors 33 | 34 | // #START:unique 35 | 36 | function getUnique(attributes) { 37 | const unique = []; 38 | for (const attribute of attributes) { 39 | if (!unique.includes(attribute)) { 40 | unique.push(attribute); 41 | } 42 | } 43 | return unique; 44 | } 45 | 46 | const colors = getColors(dogs); 47 | getUnique(colors); 48 | // ['black', 'chocolate'] 49 | 50 | // #END:unique 51 | 52 | export { getColors, getUnique }; 53 | -------------------------------------------------------------------------------- /collections/set/unique.spec.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsmapr1/simplifying-js/a7ced4922664a4ea0d2b584963fd4d7aeebd7321/collections/set/unique.spec.js -------------------------------------------------------------------------------- /conditionals/falsy/falsy.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsmapr1/simplifying-js/a7ced4922664a4ea0d2b584963fd4d7aeebd7321/conditionals/falsy/falsy.js -------------------------------------------------------------------------------- /conditionals/falsy/falsy.spec.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsmapr1/simplifying-js/a7ced4922664a4ea0d2b584963fd4d7aeebd7321/conditionals/falsy/falsy.spec.js -------------------------------------------------------------------------------- /conditionals/falsy/notFalsy.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | 3 | function hasInformation() { 4 | return !!{}; 5 | } 6 | -------------------------------------------------------------------------------- /conditionals/shortCircuiting/conditional.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | 3 | // #START:problem 4 | function getFirstImage(userConfig) { 5 | let img = 'default.png'; 6 | if (userConfig.images) { 7 | img = userConfig.images[0]; 8 | } 9 | return img; 10 | } 11 | // #END:problem 12 | 13 | // #START:problem2 14 | function getImage(userConfig) { 15 | let img = 'default.png'; 16 | if (userConfig.images) { 17 | if (userConfig.images.length) { 18 | img = userConfig.images[0]; 19 | } 20 | } 21 | return img; 22 | } 23 | // #END:problem2 24 | 25 | export { getImage }; 26 | -------------------------------------------------------------------------------- /conditionals/shortCircuiting/shortCircuiting.js: -------------------------------------------------------------------------------- 1 | // #START:or 2 | function getIconPath(icon) { 3 | const path = icon.path || 'uploads/default.png'; 4 | return `https://assets.foo.com/${path}`; 5 | } 6 | // #END:or 7 | 8 | // #START:and 9 | function getImage(userConfig) { 10 | if (userConfig.images && userConfig.images.length > 0) { 11 | return userConfig.images[0]; 12 | } 13 | return 'default.png'; 14 | } 15 | // #END:and 16 | 17 | 18 | export { getIconPath, getImage }; 19 | -------------------------------------------------------------------------------- /conditionals/shortCircuiting/ternary.js: -------------------------------------------------------------------------------- 1 | // #START:problem 2 | function getIconPath(icon) { 3 | const path = icon.path ? icon.path : 'uploads/default.png'; 4 | return `https://assets.foo.com/${path}`; 5 | } 6 | // #END:problem 7 | 8 | // #START:and 9 | function getImage(userConfig) { 10 | const images = userConfig.images; 11 | return images && images.length ? images[0] : 'default.png'; 12 | } 13 | // #END:and 14 | 15 | function getImageOneLine(userConfig) { 16 | // #START:one 17 | const images = userConfig.images; 18 | return images && 19 | images.length && 20 | images[0].indexOf('gif') < 0 21 | ? images[0] : 'default.png'; 22 | // #END:one 23 | } 24 | 25 | export { getIconPath, getImage, getImageOneLine }; 26 | -------------------------------------------------------------------------------- /conditionals/ternary/if.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable block-scoped-var, no-redeclare, no-var, no-undef, no-unused-vars, vars-on-top */ 2 | 3 | function configureTimePermissions({ title }) { 4 | if (title === 'manager') { 5 | var permissions = ['time', 'pay']; 6 | } else { 7 | var permissions = ['time']; 8 | } 9 | return permissions; 10 | } 11 | 12 | function configureTimePermissions({ title }) { 13 | // # START:let 14 | if (title === 'manager') { 15 | const permissions = ['time', 'pay']; 16 | } else { 17 | const permissions = ['time']; 18 | } 19 | permissions; 20 | // ReferenceError: permissions is not defined 21 | // # END:let 22 | return permissions; 23 | } 24 | function configureTimePermissionsScope({ title }) { 25 | // # START:scope 26 | let permissions; 27 | if (title === 'manager') { 28 | permissions = ['time', 'pay']; 29 | } else { 30 | permissions = ['time']; 31 | } 32 | // # END:scope 33 | return permissions; 34 | } 35 | 36 | export { configureTimePermissions, configureTimePermissionsScope }; 37 | -------------------------------------------------------------------------------- /conditionals/ternary/ternary.js: -------------------------------------------------------------------------------- 1 | function configureTimePermissions(title) { 2 | // # START:const 3 | const permissions = title === 'manager' ? ['time', 'pay'] : ['time']; 4 | // # END:const 5 | return permissions; 6 | } 7 | 8 | export { configureTimePermissions }; 9 | -------------------------------------------------------------------------------- /conditionals/ternary/ternary.spec.js: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | 3 | import { 4 | configureTimePermissions as configureLet, 5 | configureTimePermissionsScope as configureLetScope, 6 | } from './if'; 7 | import { configureTimePermissions as configureProblem } from './ternaryProblem'; 8 | 9 | describe('ternary ', () => { 10 | it('should not set time permissions with if and let', () => { 11 | let RefError; 12 | try { 13 | configureLet({ title: 'manager' }); 14 | } catch (error) { 15 | RefError = error instanceof ReferenceError; 16 | } 17 | expect(RefError).toEqual(true); 18 | }); 19 | 20 | it('should set time permissions with if and let', () => { 21 | expect(configureLetScope({ title: 'manager' })).toEqual(['time', 'pay']); 22 | }); 23 | 24 | it('should set manager, supervisor, employee with ternary', () => { 25 | expect(configureProblem({ title: 'manager' })).toEqual(['time', 'overtimeAuthorization', 'pay']); 26 | expect(configureProblem({ title: 'supervisor' })).toEqual(['time', 'overtimeAuthorization']); 27 | expect(configureProblem({ title: 'employee' })).toEqual(['time']); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /conditionals/ternary/ternaryProblem.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-nested-ternary, no-unused-vars */ 2 | 3 | function configureTimePermissions({ title }) { 4 | // #START:problem 5 | const permissions = title === 'supervisor' || title === 'manager' ? 6 | title === 'manager' ? 7 | ['time', 'overtimeAuthorization', 'pay'] : ['time', 'overtimeAuthorization'] 8 | : ['time']; 9 | // #END:problem 10 | return permissions; 11 | } 12 | 13 | // #START:func 14 | function getTimePermissions({ title }) { 15 | if (title === 'manager') { 16 | return ['time', 'overtimeAuthorization', 'pay']; 17 | } 18 | 19 | if (title === 'supervisor') { 20 | return ['time', 'overtimeAuthorization']; 21 | } 22 | return ['time']; 23 | } 24 | 25 | const permissions = getTimePermissions({ title: 'employee' }); 26 | // ['time'] 27 | // #END:func 28 | 29 | export { configureTimePermissions }; 30 | -------------------------------------------------------------------------------- /externalData/async/async.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | import { getArtist, getMusic, getUserPreferences } from '../promises/promises'; 3 | 4 | // START: call 5 | async function getTheme() { 6 | const { theme } = await getUserPreferences(); 7 | return theme; 8 | } 9 | // END: call 10 | 11 | // START: theme 12 | getTheme() 13 | .then(theme => { 14 | console.log(theme); 15 | }); 16 | // END: theme 17 | 18 | // START:artist 19 | async function getArtistByPreference() { 20 | const { theme } = await getUserPreferences(); 21 | const { album } = await getMusic(theme); 22 | const { artist } = await getArtist(album); 23 | return artist; 24 | } 25 | 26 | getArtistByPreference() 27 | .then(artist => { 28 | console.log(artist); 29 | }); 30 | // END:artist 31 | 32 | export { getArtistByPreference, getTheme }; 33 | -------------------------------------------------------------------------------- /externalData/async/async.spec.js: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | import { getArtistByPreference, getTheme } from './async'; 3 | import { getArtistByPreference as catchPref } from './catch'; 4 | 5 | describe('async', () => { 6 | it('should await an action', () => { 7 | return getTheme() 8 | .then(theme => { 9 | expect(theme).toEqual('dusk'); 10 | }); 11 | }); 12 | 13 | it('should await on multiples actions', () => { 14 | return getArtistByPreference() 15 | .then(artist => { 16 | expect(artist).toEqual('Brian Eno'); 17 | }); 18 | }); 19 | 20 | it('should catch an error', () => { 21 | return catchPref() 22 | .then(() => { 23 | // Hitting this block is an error. 24 | expect(false).toEqual(true); 25 | }) 26 | .catch(e => { 27 | expect(e.type).toEqual('Network error'); 28 | }); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /externalData/async/catch.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | import { getArtist, getUserPreferences, failMusic } from '../promises/promises'; 3 | // START: artist 4 | async function getArtistByPreference() { 5 | const { theme } = await getUserPreferences(); 6 | const { album } = await failMusic(theme); 7 | const { artist } = await getArtist(album); 8 | return artist; 9 | } 10 | 11 | getArtistByPreference() 12 | .then(artist => { 13 | console.log(artist); 14 | }) 15 | .catch(e => { 16 | console.error(e); 17 | }); 18 | // END: artist 19 | 20 | export { getArtistByPreference }; 21 | -------------------------------------------------------------------------------- /externalData/fetch/example.js: -------------------------------------------------------------------------------- 1 | import { setLatestPost } from './fetch'; 2 | import { getPost } from './services/postService'; 3 | 4 | const sidebar = document.getElementById('sidebar'); 5 | setLatestPost(1, sidebar, getPost); 6 | // Sets sidebar 7 | -------------------------------------------------------------------------------- /externalData/fetch/fetch.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console, no-unused-vars, no-param-reassign */ 2 | const fetch = require('node-fetch'); 3 | 4 | 5 | const example = 6 | // START:result 7 | { 8 | userId: 1, 9 | id: 1, 10 | title: 'First Post', 11 | body: 'This is my first post...', 12 | }; 13 | // END:result 14 | 15 | // START:simple 16 | fetch('https://jsonplaceholder.typicode.com/posts/1'); 17 | // END:simple 18 | 19 | // START:resolve 20 | fetch('https://jsonplaceholder.typicode.com/posts/1') 21 | .then(data => { 22 | return data.json(); 23 | }) 24 | .then(post => { 25 | console.log(post.title); 26 | }); 27 | // END:resolve 28 | 29 | // START:catch 30 | fetch('https://jsonplaceholder.typicode.com/pots/1') 31 | .then(data => { 32 | if (!data.ok) { 33 | throw Error(data.status); 34 | } 35 | return data.json(); 36 | }) 37 | .then(post => { 38 | console.log(post.title); 39 | }) 40 | .catch(e => { 41 | console.log(e); 42 | }); 43 | // END:catch 44 | 45 | // START: post 46 | const update = { 47 | title: 'Clarence White Techniques', 48 | body: 'Amazing', 49 | userId: 1, 50 | }; 51 | 52 | const options = { 53 | method: 'POST', 54 | headers: { 55 | 'Content-Type': 'application/json', 56 | }, 57 | body: JSON.stringify(update), 58 | }; 59 | 60 | fetch('https://jsonplaceholder.typicode.com/posts', options).then(data => { 61 | if (!data.ok) { 62 | throw Error(data.status); 63 | } 64 | return data.json(); 65 | }).then(update => { 66 | console.log(update); 67 | // { 68 | // title: 'Clarence White Techniques', 69 | // body: 'Amazing', 70 | // userId: 1, 71 | // id: 101 72 | // }; 73 | }).catch(e => { 74 | console.log(e); 75 | }); 76 | // END: post 77 | 78 | function getPosts() { 79 | return fetch('https://jsonplaceholder.typicode.com/posts') 80 | .then(d => { 81 | return d.json(); 82 | }); 83 | } 84 | 85 | function setLatestPost(element, retrievePosts) { 86 | return retrievePosts() 87 | .then(posts => { 88 | console.log(posts); 89 | element.innerHTML = posts[0].title; 90 | }); 91 | } 92 | 93 | export { setLatestPost }; 94 | -------------------------------------------------------------------------------- /externalData/fetch/fetch.spec.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | import expect from 'expect'; 3 | import { setLatestPost } from './posts'; 4 | 5 | describe('set latest', () => { 6 | it('should update post', () => { 7 | const title = 'Doc Watson Gets the Blues'; 8 | function fakeService() { 9 | return Promise.resolve({ title }); 10 | } 11 | 12 | const el = { 13 | innerHTML: '', 14 | }; 15 | 16 | return setLatestPost(el, 1, fakeService) 17 | .then(() => { 18 | expect(el.innerHTML).toEqual(title); 19 | }) 20 | .catch(e => { 21 | // Getting here is bad. 22 | console.log(e); 23 | expect(true).toEqual(false); 24 | }); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /externalData/fetch/posts.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-param-reassign */ 2 | function setLatestPost(element, id, retrievePost) { 3 | return retrievePost(id) 4 | .then(post => { 5 | element.innerHTML = post.title; 6 | }); 7 | } 8 | 9 | export { setLatestPost }; 10 | -------------------------------------------------------------------------------- /externalData/fetch/services/postService.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | function getPost(id) { 3 | return fetch(`https://jsonplaceholder.typicode.com/posts/${id}`) 4 | .then(data => { 5 | if (!data.ok) { 6 | throw Error(data.status); 7 | } 8 | return data.json(); 9 | }) 10 | .catch(e => { 11 | console.log(e); 12 | }); 13 | } 14 | 15 | export { getPost }; 16 | -------------------------------------------------------------------------------- /externalData/local/local.js: -------------------------------------------------------------------------------- 1 | // Include filter preferences. 2 | 3 | // START:setItem 4 | function saveBreed(breed) { 5 | localStorage.setItem('breed', breed); 6 | } 7 | // END:setItem 8 | 9 | // START:getItem 10 | function getSavedBreed() { 11 | return localStorage.getItem('breed'); 12 | } 13 | // END:getItem 14 | 15 | // START:removeItem 16 | function removeBreed() { 17 | return localStorage.removeItem('breed'); 18 | } 19 | // END:removeItem 20 | 21 | // START:applyBreed 22 | function applyBreedPreference(filters) { 23 | const breed = getSavedBreed(); 24 | if (breed) { 25 | filters.set('breed', breed); 26 | } 27 | return filters; 28 | } 29 | // END:applyBreed 30 | 31 | // START:savePref 32 | function savePreferences(filters) { 33 | const filterString = JSON.stringify([...filters]); 34 | localStorage.setItem('preferences', filterString); 35 | } 36 | // END:savePref 37 | 38 | // START:applyPref 39 | function retrievePreferences() { 40 | const preferences = JSON.parse(localStorage.getItem('preferences')); 41 | return new Map(preferences); 42 | } 43 | // END:applyPref 44 | 45 | // START:clear 46 | function clearPreferences() { 47 | localStorage.clear(); 48 | } 49 | // END:clear 50 | 51 | export { 52 | applyBreedPreference, 53 | clearPreferences, 54 | getSavedBreed, 55 | removeBreed, 56 | retrievePreferences, 57 | saveBreed, 58 | savePreferences, 59 | }; 60 | -------------------------------------------------------------------------------- /externalData/local/local.spec.js: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | import { 3 | applyBreedPreference, 4 | getSavedBreed, 5 | removeBreed, 6 | retrievePreferences, 7 | saveBreed, 8 | savePreferences, 9 | } from './local'; 10 | 11 | describe('localstorage', () => { 12 | afterEach(() => { 13 | localStorage.clear(); 14 | }); 15 | 16 | it('should set breed', () => { 17 | saveBreed('labrador'); 18 | expect(localStorage.getItem('breed')).toEqual('labrador'); 19 | }); 20 | 21 | it('should get breed', () => { 22 | saveBreed('labrador'); 23 | expect(getSavedBreed()).toEqual('labrador'); 24 | }); 25 | 26 | it('should remove breed', () => { 27 | saveBreed('labrador'); 28 | expect(localStorage.getItem('breed')).toEqual('labrador'); 29 | removeBreed(); 30 | expect(localStorage.getItem('breed')).toEqual(undefined); 31 | }); 32 | 33 | it('apply filters', () => { 34 | const defaults = new Map(); 35 | saveBreed('labrador'); 36 | const filters = applyBreedPreference(defaults); 37 | expect(filters.get('breed')).toEqual('labrador'); 38 | }); 39 | 40 | it('should save all preferences', () => { 41 | const filters = new Map() 42 | .set('color', 'black'); 43 | savePreferences(filters); 44 | expect(localStorage.getItem('preferences')).toEqual('[["color","black"]]'); 45 | }); 46 | 47 | it('should retrieve all preferences', () => { 48 | localStorage.setItem('preferences', '[["color","black"]]'); 49 | const prefences = retrievePreferences(); 50 | expect(prefences.get('color')).toEqual('black'); 51 | }); 52 | }); 53 | -------------------------------------------------------------------------------- /externalData/promises/problem.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars, no-console */ 2 | // START:cb 3 | function getUserPreferences(cb) { 4 | return setTimeout(() => { 5 | cb({ 6 | theme: 'dusk', 7 | }); 8 | }, 1000); 9 | } 10 | 11 | function log(value) { 12 | return console.log(value); 13 | } 14 | 15 | log('starting'); 16 | // starting 17 | 18 | getUserPreferences(preferences => { 19 | return log(preferences.theme.toUpperCase()); 20 | }); 21 | 22 | log('ending?'); 23 | // ending 24 | 25 | // DUSK 26 | 27 | // END:cb 28 | 29 | // START:music 30 | function getMusic(theme, cb) { 31 | return setTimeout(() => { 32 | if (theme === 'dusk') { 33 | return cb({ 34 | album: 'music for airports', 35 | }); 36 | } 37 | return cb({ 38 | album: 'kind of blue', 39 | }); 40 | }, 1000); 41 | } 42 | // END:music 43 | 44 | // START:hell 45 | getUserPreferences(preferences => { 46 | return getMusic(preferences.theme, music => { 47 | console.log(music.album); 48 | }); 49 | }); 50 | // END:hell 51 | 52 | export { getUserPreferences }; 53 | -------------------------------------------------------------------------------- /externalData/promises/promises.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars, no-console */ 2 | 3 | // START:define 4 | function getUserPreferences() { 5 | const preferences = new Promise((resolve, reject) => { 6 | resolve({ 7 | theme: 'dusk', 8 | }); 9 | }); 10 | return preferences; 11 | } 12 | // END:define 13 | 14 | // START:pref 15 | getUserPreferences() 16 | .then(preferences => { 17 | console.log(preferences.theme); 18 | }); 19 | // 'dusk' 20 | // END:pref 21 | 22 | // START:fail 23 | function failUserPreference() { 24 | const finder = new Promise((resolve, reject) => { 25 | reject({ 26 | type: 'Access Denied', 27 | }); 28 | }); 29 | return finder; 30 | } 31 | // END:fail 32 | 33 | // START:catch 34 | failUserPreference() 35 | .then(preferences => { 36 | // This won't execute 37 | console.log(preferences.theme); 38 | }) 39 | .catch(error => { 40 | console.error(`Fail: ${error.type}`); 41 | }); 42 | // Fail: Access Denied 43 | // END:catch 44 | 45 | // START:music 46 | function getMusic(theme) { 47 | if (theme === 'dusk') { 48 | return Promise.resolve({ 49 | album: 'music for airports', 50 | }); 51 | } 52 | return Promise.resolve({ 53 | album: 'kind of blue', 54 | }); 55 | } 56 | // END:music 57 | 58 | // START:chain 59 | getUserPreferences() 60 | .then(preference => { 61 | return getMusic(preference.theme); 62 | }) 63 | .then(music => { 64 | console.log(music.album); 65 | }); 66 | // music for airports 67 | // END:chain 68 | 69 | // START:arrow 70 | getUserPreferences() 71 | .then(preference => getMusic(preference.theme)) 72 | .then(music => { console.log(music.album); }); 73 | // END:arrow 74 | 75 | // START:artist 76 | function getArtist(album) { 77 | return Promise.resolve({ 78 | artist: 'Brian Eno', 79 | }); 80 | } 81 | // END:artist 82 | 83 | // START:single 84 | function failMusic(theme) { 85 | return Promise.reject({ 86 | type: 'Network error', 87 | }); 88 | } 89 | 90 | getUserPreferences() 91 | .then(preference => failMusic(preference.theme)) 92 | .then(music => getArtist(music.album)) 93 | .catch(e => { 94 | console.log(e); 95 | }); 96 | // END:single 97 | 98 | export { failUserPreference, failMusic, getArtist, getMusic, getUserPreferences }; 99 | -------------------------------------------------------------------------------- /externalData/promises/promises.spec.js: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | import { failMusic, failUserPreference, getArtist, getMusic, getUserPreferences } from './promises'; 3 | import { 4 | getUserPreferences as prefProblem, 5 | } from './problem'; 6 | 7 | describe('promises', () => { 8 | it('should resolve with data', done => { 9 | return prefProblem(data => { 10 | expect(data.theme.toUpperCase()).toEqual('DUSK'); 11 | done(); 12 | }); 13 | }); 14 | 15 | it('should resolve with data', () => { 16 | return getUserPreferences().then(data => { 17 | expect(data.theme).toEqual('dusk'); 18 | }); 19 | }); 20 | it('should fail', () => { 21 | return failUserPreference() 22 | .catch(data => { 23 | expect(data.type).toEqual('Access Denied'); 24 | }); 25 | }); 26 | it('should chain success', () => { 27 | return getUserPreferences() 28 | .then(preference => { 29 | return getMusic(preference.theme); 30 | }) 31 | .then(music => { 32 | expect(music.album).toEqual('music for airports'); 33 | }); 34 | }); 35 | 36 | it('should catch failure', () => { 37 | return getUserPreferences() 38 | .then(preference => failMusic(preference.theme)) 39 | .then(music => getArtist(music.album)) 40 | .catch(e => { 41 | expect(e.type).toEqual('Network error'); 42 | }); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /functions/arrow/arrow.js: -------------------------------------------------------------------------------- 1 | // START: name 2 | const comic = { 3 | first: 'Peter', 4 | last: 'Bagge', 5 | city: 'Seattle', 6 | state: 'Washington', 7 | }; 8 | 9 | const getName = ({ first, last }) => `${first} ${last}`; 10 | getName(comic); 11 | // Peter Bagge 12 | // END: name 13 | 14 | // START: return 15 | const getFullName = ({ first, last }) => ({ fullName: `${first} ${last}` }); 16 | getFullName(comic); 17 | // { fullName: 'Peter Bagge' } 18 | // END: return 19 | 20 | // START:multi 21 | const getNameAndLocation = ({ first, last, city, state }) => ({ 22 | fullName: `${first} ${last}`, 23 | location: `${city}, ${state}`, 24 | }); 25 | getNameAndLocation(comic); 26 | // { 27 | // fullName: 'Peter Bagge', 28 | // location: 'Seattle, Washington' 29 | // } 30 | // END:multi 31 | 32 | // START: high 33 | const discounter = discount => price => price * (1 - discount); 34 | 35 | const tenPercentOff = discounter(0.1); 36 | tenPercentOff(100); 37 | // 90; 38 | // END: high 39 | 40 | // START: together 41 | discounter(0.1)(100); 42 | // 90 43 | // END: together 44 | 45 | export { comic, discounter, getName, getNameAndLocation, getFullName }; 46 | -------------------------------------------------------------------------------- /functions/arrow/arrow.spec.js: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | 3 | import { 4 | discounter as discountProblem, 5 | getFullName as fullProblem, 6 | getName as nameProblem, 7 | name, 8 | } from './problem'; 9 | 10 | import { 11 | comic, 12 | discounter, 13 | getFullName, 14 | getName, 15 | getNameAndLocation, 16 | } from './arrow'; 17 | 18 | describe('problems', () => { 19 | it('should get a name', () => { 20 | expect(nameProblem(name)).toEqual('Lemmy Kilmister'); 21 | }); 22 | 23 | it('should return an object', () => { 24 | expect(fullProblem(name)).toEqual({ fullName: 'Lemmy Kilmister' }); 25 | }); 26 | 27 | it('should return a high order function', () => { 28 | expect(discountProblem(0.1)(100)).toEqual(90); 29 | }); 30 | }); 31 | 32 | describe('arrow functions', () => { 33 | it('should destructure inputs', () => { 34 | expect(getName(comic)).toEqual('Peter Bagge'); 35 | }); 36 | 37 | it('should return an object', () => { 38 | expect(getFullName(comic)).toEqual({ fullName: 'Peter Bagge' }); 39 | }); 40 | 41 | it('should return an object', () => { 42 | expect(getNameAndLocation(comic)).toEqual({ 43 | fullName: 'Peter Bagge', 44 | location: 'Seattle, Washington', 45 | }); 46 | }); 47 | 48 | it('should return a high order function', () => { 49 | expect(discounter(0.1)(100)).toEqual(90); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /functions/arrow/close.js: -------------------------------------------------------------------------------- 1 | const getName = { first, last } => `${first} ${last}`; 2 | 3 | // Error: Unexpected token '=>'. Expected ';' after variable declaration 4 | -------------------------------------------------------------------------------- /functions/arrow/problem.js: -------------------------------------------------------------------------------- 1 | // START: name 2 | const name = { 3 | first: 'Lemmy', 4 | last: 'Kilmister', 5 | }; 6 | 7 | function getName({ first, last }) { 8 | return `${first} ${last}`; 9 | } 10 | // END: name 11 | 12 | // START: full 13 | function getFullName({ first, last }) { 14 | return { 15 | fullName: `${first} ${last}`, 16 | }; 17 | } 18 | // END: full 19 | 20 | // START: high 21 | const discounter = discount => { 22 | return price => { 23 | return price * (1 - discount); 24 | }; 25 | }; 26 | const tenPercentOff = discounter(0.1); 27 | tenPercentOff(100); 28 | // 90 29 | // END: high 30 | 31 | export { getName, getFullName, discounter, name }; 32 | -------------------------------------------------------------------------------- /functions/context/basic.js: -------------------------------------------------------------------------------- 1 | // START:func 2 | const validator = { 3 | message: 'is invalid.', 4 | setInvalidMessage(field) { 5 | return `${field} ${this.message}`; 6 | }, 7 | }; 8 | 9 | validator.setInvalidMessage('city'); 10 | // city is invalid. 11 | // END:func 12 | 13 | export { validator }; 14 | -------------------------------------------------------------------------------- /functions/context/context.js: -------------------------------------------------------------------------------- 1 | // START: func 2 | const validator = { 3 | message: 'is invalid.', 4 | setInvalidMessages(...fields) { 5 | return fields.map(field => { 6 | return `${field} ${this.message}`; 7 | }); 8 | }, 9 | }; 10 | 11 | validator.setInvalidMessages('city'); 12 | // ['city is invalid.] 13 | // END: func 14 | 15 | export { validator }; 16 | -------------------------------------------------------------------------------- /functions/context/context.spec.js: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | 3 | import { validator } from './context'; 4 | 5 | import { 6 | validator as validatorBasic, 7 | } from './basic'; 8 | 9 | import { 10 | validator as validatorProblem, 11 | } from './problem'; 12 | 13 | import { 14 | validator as validatorMethod, 15 | } from './method'; 16 | 17 | 18 | describe('validation', () => { 19 | const field = 'city'; 20 | it('should set invalid message', () => { 21 | const message = 'city is invalid.'; 22 | expect(validatorBasic.setInvalidMessage(field)).toEqual(message); 23 | }); 24 | it('should not access context on map function', () => { 25 | try { 26 | // START: problem 27 | validatorProblem.setInvalidMessages(field); 28 | // TypeError: Cannot read property 'message' of undefined 29 | // END: problem 30 | } catch (e) { 31 | expect(e.message).toEqual("Cannot read property 'message' of undefined"); 32 | } 33 | }); 34 | 35 | it('should access context with arrow function', () => { 36 | const messages = ['city is invalid.']; 37 | expect(validator.setInvalidMessages(field)).toEqual(messages); 38 | }); 39 | 40 | it('should not access context on map function', () => { 41 | try { 42 | // START: problemMethod 43 | validatorMethod.setInvalidMessage(field); 44 | // TypeError: Cannot read property 'message' of undefined 45 | // END: problemMethod 46 | } catch (e) { 47 | expect(e.message).toEqual("Cannot read property 'message' of undefined"); 48 | } 49 | }); 50 | }); 51 | -------------------------------------------------------------------------------- /functions/context/method.js: -------------------------------------------------------------------------------- 1 | // START: func 2 | const validator = { 3 | message: 'is invalid.', 4 | setInvalidMessage: field => `${field} ${this.message}`, 5 | }; 6 | // END: func 7 | 8 | export { validator }; 9 | -------------------------------------------------------------------------------- /functions/context/problem.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars, no-param-reassign, no-console, func-names */ 2 | 3 | // START:func 4 | const validator = { 5 | message: 'is invalid.', 6 | setInvalidMessages(...fields) { 7 | return fields.map(function (field) { 8 | return `${field} ${this.message}`; 9 | }); 10 | }, 11 | }; 12 | // END:func 13 | 14 | export { validator }; 15 | -------------------------------------------------------------------------------- /functions/curry/curry.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | // START:dogs 3 | const dogs = [ 4 | { 5 | name: 'max', 6 | weight: 10, 7 | breed: 'boston terrier', 8 | state: 'wisconsin', 9 | color: 'black', 10 | }, 11 | { 12 | name: 'don', 13 | weight: 90, 14 | breed: 'labrador', 15 | state: 'kansas', 16 | color: 'black', 17 | }, 18 | { 19 | name: 'shadow', 20 | weight: 40, 21 | breed: 'labrador', 22 | state: 'wisconsin', 23 | color: 'chocolate', 24 | }, 25 | ]; 26 | // END:dogs 27 | 28 | // START: func 29 | function getDogNames(dogs, filterFunc) { 30 | return dogs 31 | .filter(filterFunc) 32 | .map(dog => dog.name) 33 | } 34 | 35 | getDogNames(dogs, dog => dog.weight < 20); 36 | // ['max'] 37 | // END: func 38 | 39 | // START:curry 40 | const weightCheck = weight => dog => dog.weight < weight; 41 | 42 | getDogNames(dogs, weightCheck(20)); 43 | // ['max'] 44 | 45 | getDogNames(dogs, weightCheck(50)); 46 | // ['max', 'shadow'] 47 | // END:curry 48 | 49 | // START:identity 50 | const identity = field => value => dog => dog[field] === value; 51 | const colorCheck = identity('color'); 52 | const stateCheck = identity('state'); 53 | 54 | getDogNames(dogs, colorCheck('chocolate')); 55 | // ['shadow'] 56 | 57 | getDogNames(dogs, stateCheck('kansas')); 58 | // ['don'] 59 | // END:identity 60 | 61 | // START:all 62 | function allFilters(dogs, ...checks) { 63 | return dogs 64 | .filter(dog => checks.every(check => check(dog))) 65 | .map(dog => dog.name); 66 | } 67 | allFilters(dogs, colorCheck('black'), stateCheck('kansas')); 68 | // ['Don'] 69 | 70 | function anyFilters(dogs, ...checks) { 71 | return dogs 72 | .filter(dog => checks.some(check => check(dog))) 73 | .map(dog => dog.name); 74 | } 75 | 76 | anyFilters(dogs, weightCheck(20), colorCheck('chocolate')); 77 | // ['max', 'shadow'] 78 | // END:all 79 | 80 | export { anyFilters, dogs, getDogNames, colorCheck, weightCheck, stateCheck, allFilters } 81 | -------------------------------------------------------------------------------- /functions/curry/curry.spec.js: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | 3 | import { 4 | getDogNames as getNamesFull, 5 | } from './problem'; 6 | 7 | import { 8 | allFilters, 9 | anyFilters, 10 | dogs, 11 | getDogNames, 12 | colorCheck, 13 | stateCheck, 14 | weightCheck, 15 | } from './curry'; 16 | 17 | describe('curry', () => { 18 | it('should apply a function without currying', () => { 19 | expect(getNamesFull(dogs, ['color', 'black'])).toEqual(['max', 'don']); 20 | }); 21 | it('should get dog names with regular function', () => { 22 | expect(getDogNames(dogs, dog => dog.color === 'black')).toEqual(['max', 'don']); 23 | }); 24 | it('should get dog names with curried function', () => { 25 | expect(getDogNames(dogs, weightCheck(20))).toEqual(['max']); 26 | }); 27 | it('should get dog names with curried function', () => { 28 | expect(getDogNames(dogs, weightCheck(20))).toEqual(['max']); 29 | }); 30 | it('should apply multiple functions', () => { 31 | expect(allFilters(dogs, colorCheck('black'), stateCheck('kansas'))).toEqual(['don']); 32 | }); 33 | it('should apply multiple functions', () => { 34 | expect(allFilters(dogs, colorCheck('black'), stateCheck('kansas'))).toEqual(['don']); 35 | }); 36 | it('should apply multiple functions', () => { 37 | expect(anyFilters(dogs, weightCheck(20), colorCheck('chocolate'))).toEqual(['max', 'shadow']); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /functions/curry/curry.spec2.js: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | 3 | import { 4 | getDogNames as getNamesFull, 5 | } from './problem'; 6 | 7 | import { 8 | applyFilters, 9 | anyFilters, 10 | dogs, 11 | getDogNames, 12 | colorCheck, 13 | stateCheck, 14 | weightCheck, 15 | } from './curry'; 16 | 17 | describe('curry', () => { 18 | it('should apply a function without currying', () => { 19 | expect(getNamesFull(dogs, ['color', 'black'])).toEqual(['max', 'don']); 20 | }); 21 | it('should get dog names with regular function', () => { 22 | expect(getDogNames(dogs, dog => dog.color === 'black')).toEqual(['max', 'don']); 23 | }); 24 | it('should get dog names with curried function', () => { 25 | expect(getDogNames(dogs, weightCheck(20))).toEqual(['max']); 26 | }); 27 | it('should get dog names with curried function', () => { 28 | expect(getDogNames(dogs, weightCheck(20))).toEqual(['max']); 29 | }); 30 | it('should apply multiple functions', () => { 31 | expect(applyFilters(dogs, colorCheck('black'), stateCheck('kansas'))).toEqual(['don']); 32 | }); 33 | it('should apply multiple functions', () => { 34 | expect(applyFilters(dogs, colorCheck('black'), stateCheck('kansas'))).toEqual(['don']); 35 | }); 36 | it('should apply multiple functions', () => { 37 | expect(anyFilters(dogs, weightCheck(20), colorCheck('chocolate'))).toEqual(['max', 'shadow']); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /functions/curry/highorder.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | // START:program 4 | const setStrongHallProgram = program => { 5 | const defaults = { 6 | hours: '8 a.m. - 8 p.m.', 7 | address: 'Jayhawk Blvd', 8 | name: 'Augusto', 9 | phone: '555-555-5555' 10 | } 11 | return { ...defaults, ...program} 12 | } 13 | 14 | const programs = setStrongHallProgram(program); 15 | 16 | const exhibit = setStrongHallProgram(exhibit); 17 | // END:program 18 | -------------------------------------------------------------------------------- /functions/curry/problem.js: -------------------------------------------------------------------------------- 1 | import { dogs } from './curry'; 2 | // START: func 3 | function getDogNames(dogs, filter) { 4 | const [key, value] = filter; 5 | return dogs 6 | .filter(dog => dog[key] === value) 7 | .map(dog => dog.name); 8 | } 9 | 10 | getDogNames(dogs, ['color', 'black']); 11 | // ['max', 'don'] 12 | // END: func 13 | 14 | export { getDogNames }; 15 | -------------------------------------------------------------------------------- /functions/oldTest/problem.js: -------------------------------------------------------------------------------- 1 | import { redirect } from './routing'; 2 | import { getTaxInformation } from './taxService'; 3 | 4 | function formatPrice({ price, location }) { 5 | const user = document.getElementById('user').innerHTML; //