├── .gitignore
├── README.md
├── diagrams
├── 20
│ ├── .gitkeep
│ └── diagrams.xml
├── 21
│ ├── .gitkeep
│ └── diagrams.xml
├── 22
│ └── .gitkeep
├── 23
│ └── .gitkeep
├── 01
│ ├── .gitkeep
│ └── diagrams.xml
├── 02
│ └── .gitkeep
├── 03
│ └── .gitkeep
├── 04
│ └── .gitkeep
├── 05
│ └── .gitkeep
├── async
│ ├── .gitkeep
│ ├── reqs.xml
│ └── requests.xml
├── ecomm
│ ├── .gitkeep
│ ├── diagrams.xml
│ └── products.xml
├── maze
│ ├── .gitkeep
│ └── diagrams.xml
├── message
│ ├── .gitkeep
│ └── diagrams.xml
├── movies
│ ├── .gitkeep
│ ├── diagrams.xml
│ └── part2.xml
├── node
│ ├── .gitkeep
│ ├── async.xml
│ ├── diagrams.xml
│ └── ls.xml
├── promises
│ ├── .gitkeep
│ └── promises.xml
├── requests
│ ├── .gitkeep
│ └── diagrams.xml
├── testing
│ ├── .gitkeep
│ └── testing.xml
└── timer
│ ├── .gitkeep
│ └── diagrams.xml
├── ecomm
├── carts.json
├── index.js
├── package-lock.json
├── package.json
├── products.json
├── public
│ ├── css
│ │ └── main.css
│ └── images
│ │ └── banner.jpg
├── repositories
│ ├── carts.js
│ ├── products.js
│ ├── repository.js
│ └── users.js
├── routes
│ ├── admin
│ │ ├── auth.js
│ │ ├── middlewares.js
│ │ ├── products.js
│ │ └── validators.js
│ ├── carts.js
│ └── products.js
├── users.json
└── views
│ ├── admin
│ ├── auth
│ │ ├── signin.js
│ │ └── signup.js
│ ├── layout.js
│ └── products
│ │ ├── edit.js
│ │ ├── index.js
│ │ └── new.js
│ ├── carts
│ └── show.js
│ ├── helpers.js
│ ├── layout.js
│ └── products
│ └── index.js
├── hidash
├── index.js
└── index.test.js
├── list
├── index.js
├── package-lock.json
├── package.json
└── test.js
├── maze
├── index.html
└── index.js
├── message
├── index.html
└── index.js
├── movies-testing
├── autocomplete.js
├── index.html
├── index.js
├── style.css
├── test
│ ├── autocomplete.test.js
│ └── test.html
└── utils.js
├── movies
├── autocomplete.js
├── index.html
├── index.js
├── style.css
└── utils.js
├── timer
├── index.html
├── index.js
├── style.css
└── timer.js
├── tme
├── index.js
├── package-lock.json
├── package.json
├── render.js
├── runner.js
├── sampleproject
│ ├── index.js
│ └── test
│ │ └── forEach.test.js
└── samplewebproject
│ ├── index.html
│ ├── index.js
│ └── test
│ └── app.test.js
└── watchit
├── index.js
├── package-lock.json
├── package.json
└── test.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # js-casts
2 | Companion repo to a course hosted on Udemy.com
3 |
--------------------------------------------------------------------------------
/diagrams/01/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StephenGrider/js-casts/f5279e79c5f1d91125b9e8a76d53452960c667f0/diagrams/01/.gitkeep
--------------------------------------------------------------------------------
/diagrams/01/diagrams.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/diagrams/02/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StephenGrider/js-casts/f5279e79c5f1d91125b9e8a76d53452960c667f0/diagrams/02/.gitkeep
--------------------------------------------------------------------------------
/diagrams/03/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StephenGrider/js-casts/f5279e79c5f1d91125b9e8a76d53452960c667f0/diagrams/03/.gitkeep
--------------------------------------------------------------------------------
/diagrams/04/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StephenGrider/js-casts/f5279e79c5f1d91125b9e8a76d53452960c667f0/diagrams/04/.gitkeep
--------------------------------------------------------------------------------
/diagrams/05/.gitkeep:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/diagrams/20/.gitkeep:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/diagrams/21/.gitkeep:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/diagrams/21/diagrams.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
--------------------------------------------------------------------------------
/diagrams/22/.gitkeep:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/diagrams/23/.gitkeep:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/diagrams/async/.gitkeep:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/diagrams/ecomm/.gitkeep:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/diagrams/maze/.gitkeep:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/diagrams/message/.gitkeep:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/diagrams/movies/.gitkeep:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/diagrams/movies/part2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
--------------------------------------------------------------------------------
/diagrams/node/.gitkeep:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/diagrams/node/async.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
--------------------------------------------------------------------------------
/diagrams/promises/.gitkeep:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/diagrams/requests/.gitkeep:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/diagrams/testing/.gitkeep:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/diagrams/timer/.gitkeep:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/ecomm/carts.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "items": [],
4 | "id": "cafda8fb"
5 | },
6 | {
7 | "items": [
8 | {
9 | "id": "f6b1fbbc",
10 | "quantity": 1
11 | },
12 | {
13 | "id": "ed1a0390",
14 | "quantity": 1
15 | }
16 | ],
17 | "id": "fb1e6e85"
18 | }
19 | ]
--------------------------------------------------------------------------------
/ecomm/index.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const bodyParser = require('body-parser');
3 | const cookieSession = require('cookie-session');
4 | const authRouter = require('./routes/admin/auth');
5 | const adminProductsRouter = require('./routes/admin/products');
6 | const productsRouter = require('./routes/products');
7 | const cartsRouter = require('./routes/carts');
8 |
9 | const app = express();
10 |
11 | app.use(express.static('public'));
12 | app.use(bodyParser.urlencoded({ extended: true }));
13 | app.use(
14 | cookieSession({
15 | keys: ['lkasld235j']
16 | })
17 | );
18 | app.use(authRouter);
19 | app.use(productsRouter);
20 | app.use(adminProductsRouter);
21 | app.use(cartsRouter);
22 |
23 | app.listen(3000, () => {
24 | console.log('Listening');
25 | });
26 |
--------------------------------------------------------------------------------
/ecomm/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ecomm",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "dev": "nodemon index.js --ignore *.json"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "cookie-session": "^1.3.3",
14 | "express": "^4.17.1",
15 | "express-validator": "^6.2.0",
16 | "multer": "^1.4.2",
17 | "nodemon": "^1.19.4"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/ecomm/public/css/main.css:
--------------------------------------------------------------------------------
1 | body {
2 | --primary: #631d76;
3 | --secondary: #9e4770;
4 | --white: #fbfbfb;
5 | --dark: #2e2532;
6 | --black: #201a23;
7 | }
8 |
9 | .product-card figure {
10 | text-align: center;
11 | padding: 5px;
12 | }
13 |
14 | .navbar ul.social {
15 | display: flex;
16 | }
17 | .navbar ul.social li {
18 | margin-right: 20px;
19 | text-decoration: none;
20 | }
21 | .navbar ul.social li a {
22 | color: var(--white);
23 | }
24 |
25 | .banner {
26 | margin: 50px;
27 | }
28 |
29 | .columns.products {
30 | flex-wrap: wrap;
31 | }
32 |
33 | .card-footer-item i {
34 | margin-right: 10px;
35 | }
36 |
37 | .navbar-bottom.navbar {
38 | align-items: center;
39 | }
40 |
41 | .navbar-top.navbar {
42 | background-color: var(--primary);
43 | }
44 |
45 | .navbar-buttons {
46 | display: flex;
47 | }
48 |
49 | .navbar-top .container.navbar-container,
50 | .navbar-bottom .container.navbar-container {
51 | display: flex;
52 | justify-content: space-between;
53 | padding: 0px 5%;
54 | align-items: center;
55 | }
56 |
57 | #cart h3 {
58 | margin-top: 50px;
59 | }
60 |
61 | #cart .cart-item {
62 | margin-bottom: 10px;
63 | padding: 20px;
64 | }
65 |
66 | .product-card .card-footer {
67 | justify-content: center;
68 | }
69 | .product-card .card-footer form {
70 | display: block;
71 | width: 100%;
72 | }
73 |
74 | .admin > div.container {
75 | margin: 40px 10%;
76 | }
77 |
78 | .admin .control {
79 | display: flex;
80 | justify-content: space-between;
81 | align-items: center;
82 | }
83 |
84 | .admin table {
85 | width: 100%;
86 | }
87 |
88 | .admin table th:first-of-type {
89 | width: 50%;
90 | }
91 | .admin table td:first-of-type {
92 | width: 50%;
93 | }
94 |
95 | .admin table td {
96 | vertical-align: middle;
97 | }
98 |
99 | .product-card .card-footer button {
100 | border: none;
101 | width: 100%;
102 | height: 50px;
103 | }
104 |
105 | .product-card .card-footer button i {
106 | margin-right: 10px;
107 | }
108 |
109 | #cart .cart-item h3 {
110 | margin: 0;
111 | }
112 |
113 | .cart-item {
114 | display: flex;
115 | justify-content: space-between;
116 | align-items: center;
117 | }
118 |
119 | .quantity {
120 | position: relative;
121 | }
122 |
123 | .quantity:before {
124 | content: '#';
125 | position: absolute;
126 | z-index: 10;
127 | top: 0;
128 | right: 5px;
129 | bottom: 0;
130 | display: flex;
131 | align-items: center;
132 | color: gray;
133 | }
134 |
135 | .cart-right .field {
136 | margin-bottom: 0 !important;
137 | }
138 |
139 | .price {
140 | margin: 0 20px;
141 | }
142 |
143 | .cart-right {
144 | display: flex;
145 | align-items: center;
146 | }
147 |
--------------------------------------------------------------------------------
/ecomm/public/images/banner.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StephenGrider/js-casts/f5279e79c5f1d91125b9e8a76d53452960c667f0/ecomm/public/images/banner.jpg
--------------------------------------------------------------------------------
/ecomm/repositories/carts.js:
--------------------------------------------------------------------------------
1 | const Repository = require('./repository');
2 |
3 | class CartsRepository extends Repository {}
4 |
5 | module.exports = new CartsRepository('carts.json');
6 |
--------------------------------------------------------------------------------
/ecomm/repositories/products.js:
--------------------------------------------------------------------------------
1 | const Repository = require('./repository');
2 |
3 | class ProductsRepository extends Repository {}
4 |
5 | module.exports = new ProductsRepository('products.json');
6 |
--------------------------------------------------------------------------------
/ecomm/repositories/repository.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const crypto = require('crypto');
3 |
4 | module.exports = class Repository {
5 | constructor(filename) {
6 | if (!filename) {
7 | throw new Error('Creating a repository requires a filename');
8 | }
9 |
10 | this.filename = filename;
11 | try {
12 | fs.accessSync(this.filename);
13 | } catch (err) {
14 | fs.writeFileSync(this.filename, '[]');
15 | }
16 | }
17 |
18 | async create(attrs) {
19 | attrs.id = this.randomId();
20 |
21 | const records = await this.getAll();
22 | records.push(attrs);
23 | await this.writeAll(records);
24 |
25 | return attrs;
26 | }
27 |
28 | async getAll() {
29 | return JSON.parse(
30 | await fs.promises.readFile(this.filename, {
31 | encoding: 'utf8'
32 | })
33 | );
34 | }
35 |
36 | async writeAll(records) {
37 | await fs.promises.writeFile(
38 | this.filename,
39 | JSON.stringify(records, null, 2)
40 | );
41 | }
42 |
43 | randomId() {
44 | return crypto.randomBytes(4).toString('hex');
45 | }
46 |
47 | async getOne(id) {
48 | const records = await this.getAll();
49 | return records.find(record => record.id === id);
50 | }
51 |
52 | async delete(id) {
53 | const records = await this.getAll();
54 | const filteredRecords = records.filter(record => record.id !== id);
55 | await this.writeAll(filteredRecords);
56 | }
57 |
58 | async update(id, attrs) {
59 | const records = await this.getAll();
60 | const record = records.find(record => record.id === id);
61 |
62 | if (!record) {
63 | throw new Error(`Record with id ${id} not found`);
64 | }
65 |
66 | Object.assign(record, attrs);
67 | await this.writeAll(records);
68 | }
69 |
70 | async getOneBy(filters) {
71 | const records = await this.getAll();
72 |
73 | for (let record of records) {
74 | let found = true;
75 |
76 | for (let key in filters) {
77 | if (record[key] !== filters[key]) {
78 | found = false;
79 | }
80 | }
81 |
82 | if (found) {
83 | return record;
84 | }
85 | }
86 | }
87 | };
88 |
--------------------------------------------------------------------------------
/ecomm/repositories/users.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const crypto = require('crypto');
3 | const util = require('util');
4 | const Repository = require('./repository');
5 |
6 | const scrypt = util.promisify(crypto.scrypt);
7 |
8 | class UsersRepository extends Repository {
9 | async comparePasswords(saved, supplied) {
10 | // Saved -> password saved in our database. 'hashed.salt'
11 | // Supplied -> password given to us by a user trying sign in
12 | const [hashed, salt] = saved.split('.');
13 | const hashedSuppliedBuf = await scrypt(supplied, salt, 64);
14 |
15 | return hashed === hashedSuppliedBuf.toString('hex');
16 | }
17 |
18 | async create(attrs) {
19 | attrs.id = this.randomId();
20 |
21 | const salt = crypto.randomBytes(8).toString('hex');
22 | const buf = await scrypt(attrs.password, salt, 64);
23 |
24 | const records = await this.getAll();
25 | const record = {
26 | ...attrs,
27 | password: `${buf.toString('hex')}.${salt}`
28 | };
29 | records.push(record);
30 |
31 | await this.writeAll(records);
32 |
33 | return record;
34 | }
35 | }
36 |
37 | module.exports = new UsersRepository('users.json');
38 |
--------------------------------------------------------------------------------
/ecomm/routes/admin/auth.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 |
3 | const { handleErrors } = require('./middlewares');
4 | const usersRepo = require('../../repositories/users');
5 | const signupTemplate = require('../../views/admin/auth/signup');
6 | const signinTemplate = require('../../views/admin/auth/signin');
7 | const {
8 | requireEmail,
9 | requirePassword,
10 | requirePasswordConfirmation,
11 | requireEmailExists,
12 | requireValidPasswordForUser
13 | } = require('./validators');
14 |
15 | const router = express.Router();
16 |
17 | router.get('/signup', (req, res) => {
18 | res.send(signupTemplate({ req }));
19 | });
20 |
21 | router.post(
22 | '/signup',
23 | [requireEmail, requirePassword, requirePasswordConfirmation],
24 | handleErrors(signupTemplate),
25 | async (req, res) => {
26 | const { email, password } = req.body;
27 | const user = await usersRepo.create({ email, password });
28 |
29 | req.session.userId = user.id;
30 |
31 | res.redirect('/admin/products');
32 | }
33 | );
34 |
35 | router.get('/signout', (req, res) => {
36 | req.session = null;
37 | res.send('You are logged out');
38 | });
39 |
40 | router.get('/signin', (req, res) => {
41 | res.send(signinTemplate({}));
42 | });
43 |
44 | router.post(
45 | '/signin',
46 | [requireEmailExists, requireValidPasswordForUser],
47 | handleErrors(signinTemplate),
48 | async (req, res) => {
49 | const { email } = req.body;
50 |
51 | const user = await usersRepo.getOneBy({ email });
52 |
53 | req.session.userId = user.id;
54 |
55 | res.redirect('/admin/products');
56 | }
57 | );
58 |
59 | module.exports = router;
60 |
--------------------------------------------------------------------------------
/ecomm/routes/admin/middlewares.js:
--------------------------------------------------------------------------------
1 | const { validationResult } = require('express-validator');
2 |
3 | module.exports = {
4 | handleErrors(templateFunc, dataCb) {
5 | return async (req, res, next) => {
6 | const errors = validationResult(req);
7 |
8 | if (!errors.isEmpty()) {
9 | let data = {};
10 | if (dataCb) {
11 | data = await dataCb(req);
12 | }
13 |
14 | return res.send(templateFunc({ errors, ...data }));
15 | }
16 |
17 | next();
18 | };
19 | },
20 | requireAuth(req, res, next) {
21 | if (!req.session.userId) {
22 | return res.redirect('/signin');
23 | }
24 |
25 | next();
26 | }
27 | };
28 |
--------------------------------------------------------------------------------
/ecomm/routes/admin/products.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const multer = require('multer');
3 |
4 | const { handleErrors, requireAuth } = require('./middlewares');
5 | const productsRepo = require('../../repositories/products');
6 | const productsNewTemplate = require('../../views/admin/products/new');
7 | const productsIndexTemplate = require('../../views/admin/products/index');
8 | const productsEditTemplate = require('../../views/admin/products/edit');
9 | const { requireTitle, requirePrice } = require('./validators');
10 |
11 | const router = express.Router();
12 | const upload = multer({ storage: multer.memoryStorage() });
13 |
14 | router.get('/admin/products', requireAuth, async (req, res) => {
15 | const products = await productsRepo.getAll();
16 | res.send(productsIndexTemplate({ products }));
17 | });
18 |
19 | router.get('/admin/products/new', requireAuth, (req, res) => {
20 | res.send(productsNewTemplate({}));
21 | });
22 |
23 | router.post(
24 | '/admin/products/new',
25 | requireAuth,
26 | upload.single('image'),
27 | [requireTitle, requirePrice],
28 | handleErrors(productsNewTemplate),
29 | async (req, res) => {
30 | const image = req.file.buffer.toString('base64');
31 | const { title, price } = req.body;
32 | await productsRepo.create({ title, price, image });
33 |
34 | res.redirect('/admin/products');
35 | }
36 | );
37 |
38 | router.get('/admin/products/:id/edit', requireAuth, async (req, res) => {
39 | const product = await productsRepo.getOne(req.params.id);
40 |
41 | if (!product) {
42 | return res.send('Product not found');
43 | }
44 |
45 | res.send(productsEditTemplate({ product }));
46 | });
47 |
48 | router.post(
49 | '/admin/products/:id/edit',
50 | requireAuth,
51 | upload.single('image'),
52 | [requireTitle, requirePrice],
53 | handleErrors(productsEditTemplate, async req => {
54 | const product = await productsRepo.getOne(req.params.id);
55 | return { product };
56 | }),
57 | async (req, res) => {
58 | const changes = req.body;
59 |
60 | if (req.file) {
61 | changes.image = req.file.buffer.toString('base64');
62 | }
63 |
64 | try {
65 | await productsRepo.update(req.params.id, changes);
66 | } catch (err) {
67 | return res.send('Could not find item');
68 | }
69 |
70 | res.redirect('/admin/products');
71 | }
72 | );
73 |
74 | router.post('/admin/products/:id/delete', requireAuth, async (req, res) => {
75 | await productsRepo.delete(req.params.id);
76 |
77 | res.redirect('/admin/products');
78 | });
79 |
80 | module.exports = router;
81 |
--------------------------------------------------------------------------------
/ecomm/routes/admin/validators.js:
--------------------------------------------------------------------------------
1 | const { check } = require('express-validator');
2 | const usersRepo = require('../../repositories/users');
3 |
4 | module.exports = {
5 | requireTitle: check('title')
6 | .trim()
7 | .isLength({ min: 5, max: 40 })
8 | .withMessage('Must be between 5 and 40 characters'),
9 | requirePrice: check('price')
10 | .trim()
11 | .toFloat()
12 | .isFloat({ min: 1 })
13 | .withMessage('Must be a number greater than 1'),
14 | requireEmail: check('email')
15 | .trim()
16 | .normalizeEmail()
17 | .isEmail()
18 | .withMessage('Must be a valid email')
19 | .custom(async email => {
20 | const existingUser = await usersRepo.getOneBy({ email });
21 | if (existingUser) {
22 | throw new Error('Email in use');
23 | }
24 | }),
25 | requirePassword: check('password')
26 | .trim()
27 | .isLength({ min: 4, max: 20 })
28 | .withMessage('Must be between 4 and 20 characters'),
29 | requirePasswordConfirmation: check('passwordConfirmation')
30 | .trim()
31 | .isLength({ min: 4, max: 20 })
32 | .withMessage('Must be between 4 and 20 characters')
33 | .custom(async (passwordConfirmation, { req }) => {
34 | if (passwordConfirmation !== req.body.password) {
35 | throw new Error('Passwords must match');
36 | }
37 | }),
38 | requireEmailExists: check('email')
39 | .trim()
40 | .normalizeEmail()
41 | .isEmail()
42 | .withMessage('Must provide a valid email')
43 | .custom(async email => {
44 | const user = await usersRepo.getOneBy({ email });
45 | if (!user) {
46 | throw new Error('Email not found!');
47 | }
48 | }),
49 | requireValidPasswordForUser: check('password')
50 | .trim()
51 | .custom(async (password, { req }) => {
52 | const user = await usersRepo.getOneBy({ email: req.body.email });
53 | if (!user) {
54 | throw new Error('Invalid password');
55 | }
56 |
57 | const validPassword = await usersRepo.comparePasswords(
58 | user.password,
59 | password
60 | );
61 | if (!validPassword) {
62 | throw new Error('Invalid password');
63 | }
64 | })
65 | };
66 |
--------------------------------------------------------------------------------
/ecomm/routes/carts.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const cartsRepo = require('../repositories/carts');
3 | const productsRepo = require('../repositories/products');
4 | const cartShowTemplate = require('../views/carts/show');
5 |
6 | const router = express.Router();
7 |
8 | // Receive a post request to add an item to a cart
9 | router.post('/cart/products', async (req, res) => {
10 | // Figure out the cart!
11 | let cart;
12 | if (!req.session.cartId) {
13 | // We dont have a cart, we need to create one,
14 | // and store the cart id on the req.session.cartId
15 | // property
16 | cart = await cartsRepo.create({ items: [] });
17 | req.session.cartId = cart.id;
18 | } else {
19 | // We have a cart! Lets get it from the repository
20 | cart = await cartsRepo.getOne(req.session.cartId);
21 | }
22 |
23 | const existingItem = cart.items.find(item => item.id === req.body.productId);
24 | if (existingItem) {
25 | // increment quantity and save cart
26 | existingItem.quantity++;
27 | } else {
28 | // add new product id to items array
29 | cart.items.push({ id: req.body.productId, quantity: 1 });
30 | }
31 | await cartsRepo.update(cart.id, {
32 | items: cart.items
33 | });
34 |
35 | res.redirect('/cart');
36 | });
37 |
38 | // Receive a GET request to show all items in cart
39 | router.get('/cart', async (req, res) => {
40 | if (!req.session.cartId) {
41 | return res.redirect('/');
42 | }
43 |
44 | const cart = await cartsRepo.getOne(req.session.cartId);
45 | for (let item of cart.items) {
46 | const product = await productsRepo.getOne(item.id);
47 |
48 | item.product = product;
49 | }
50 |
51 | res.send(cartShowTemplate({ items: cart.items }));
52 | });
53 |
54 | // Receive a post request to delete an item from a cart
55 | router.post('/cart/products/delete', async (req, res) => {
56 | const { itemId } = req.body;
57 | const cart = await cartsRepo.getOne(req.session.cartId);
58 |
59 | const items = cart.items.filter(item => item.id !== itemId);
60 |
61 | await cartsRepo.update(req.session.cartId, { items });
62 |
63 | res.redirect('/cart');
64 | });
65 |
66 | module.exports = router;
67 |
--------------------------------------------------------------------------------
/ecomm/routes/products.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const productsRepo = require('../repositories/products');
3 | const productsIndexTemplate = require('../views/products/index');
4 |
5 | const router = express.Router();
6 |
7 | router.get('/', async (req, res) => {
8 | const products = await productsRepo.getAll();
9 | res.send(productsIndexTemplate({ products }));
10 | });
11 |
12 | module.exports = router;
13 |
--------------------------------------------------------------------------------
/ecomm/users.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "email": "test@test.com"
4 | },
5 | {
6 | "email": "asdf@aslkdfj.com",
7 | "password": "password",
8 | "id": "4cad493a"
9 | },
10 | {
11 | "email": "test1@test.com",
12 | "password": "password",
13 | "id": "a8a361ce"
14 | },
15 | {
16 | "email": "test3@test.com",
17 | "password": "password",
18 | "id": "3ced88e5"
19 | },
20 | {
21 | "email": "test5@test.com",
22 | "password": "ca43d32660d309b48ca04a03adaf4ebd6b877eab6110de20945669276bb6fc240a662205ebb0d7d2d05fa22fb329b35aaca3cb50356f3d4f355f8b89a084ab0a.2d21ade5cf256a9d",
23 | "id": "29aff662"
24 | },
25 | {
26 | "email": "test6@test.com",
27 | "password": "6c976960f74031c397fbf9df460cbb774d4d1ec9db68d4fc7dcc247b721542996e8e0cd82c5792305d4a3356d16ceccd6ad44ade17dbeb19efa3d6f92350d389.b8b3b0f5c49ebca7",
28 | "id": "2952bf5c"
29 | },
30 | {
31 | "email": "test10@test.com",
32 | "password": "4211bfcad8d3def24d04b9d88d4a08cdff7fefab77974c86e0e9394ae1ab9a6061f2dbf6d3389f3e9aa870f65e9070e88537f7f89af3d861705c89b46558a342.76e8d9f3f6b98cd7",
33 | "id": "7f49b927"
34 | },
35 | {
36 | "email": "",
37 | "password": "3e1480674f7e589abf3eb8114ea11eb89256dda26d7af9e7c786b2d77f945f1c5ff47b52c601b4547bd1e050f84b3735547e1dd16cd62705c717e55e9eec81eb.500208f6a9ae8466",
38 | "id": "e99f8d2e"
39 | },
40 | {
41 | "email": "test3@test.com",
42 | "password": "89ccc296b12e135729c9d2e2ac15f61550766e3e06e64642b85a2e3748f175b8b5e1a93881be99c6c5d1b933d41ba1d6cf01ca988b5a17fbc0dbb370f127c1fa.b0b86722b83f78c8",
43 | "id": "8525c8ba"
44 | },
45 | {
46 | "email": "test3@test.com",
47 | "password": "0737da26a1fecf6cc61bb1c138158d4097ec2e4fbe57be135a0596f7eb88a15cb3bdff5af7a60cc90c50c9aacbb7529dda244c36b60e65e3d92203c6e717470c.a398fef03dcd63e0",
48 | "id": "c1934981"
49 | },
50 | {
51 | "email": "test3@test.com",
52 | "password": "0a9741d02fde251d56c66ffd2cef9e8c792c83e7e763f31f60efd28b5c2f27cf4b29f28047f97bb128bc9d55518ff6f5a44b040463573a595805ef9d5e04e4be.e9657e687e491022",
53 | "id": "3abc2f01"
54 | },
55 | {
56 | "email": "alskjdf@",
57 | "password": "61a689c8d20fdc3c0738e024a50c98e9589e250bab11cc07ebaf6134f15cf04df2600e1c07dd0ff81705250df92991675882f02fff5d8afa53298723b6c20c93.dd652bc403276b8f",
58 | "id": "1d42b5f0"
59 | },
60 | {
61 | "email": "alskjdf@",
62 | "password": "a0ad5ce6b6b1df11457e705190294dbb39f43379de2ed46b2562125d9956c8548040dfcb21181463e49641e83acf706782f6732e51e7682437d3149af666bbf7.d62ca7e0b9553f70",
63 | "id": "3cdaa735"
64 | },
65 | {
66 | "email": "alskjdf@",
67 | "password": "170b1ba39186e56f489a6298799778c8c39af3ea73eb2e08d2a37506725609aac512b2f349cdfdad124969adfa3834c123ca9e75fbc17a6fbeb3586853a3786a.b58292562636f913",
68 | "id": "64f909f7"
69 | },
70 | {
71 | "email": "alksdfjlk@alskdjf.com",
72 | "password": "296f5fdfdb36cf989f8fff0654f3a5a870cee48456f6de6dc9521c94f8162054c0b958b64b4645d9663d4b4fa4522b4394d737d5de53afe4dd51d400a9f22a92.539498a95547a04d",
73 | "id": "a301383d"
74 | },
75 | {
76 | "email": "alskjdf@alksdjf.com",
77 | "password": "a0f78630e3de6b1d7c44c7f91f0fe0c2ef51f1a39e094a4c5427904e2ffe27cf5d5ef5a2042cab528a906516074b94a37734d4651d642d1c1f0e3e35de23b8fd.637c5aa7b3388258",
78 | "id": "10774d9d"
79 | },
80 | {
81 | "email": "alskdjf@alskdjf.com",
82 | "password": "489660662a0210cbf0f82b160030a075138deeacca0862eb8406db9624427c9d47546bfa8c577d03503a6128c386d281647a7998ac99b0180dad9b88e57cc958.4a4a343f5a6e0efa",
83 | "id": "b4214114"
84 | },
85 | {
86 | "email": "test1935@test.com",
87 | "password": "695e1c471599ac9dcae7bba80ae65c3ad5bf0e3d57368047a0965f2c9be3020841e48e7f93873417a0b7799328614cca2699692235c38543575ef93cdb32adc1.c52032cb2ca6d17e",
88 | "id": "2e90d67b"
89 | },
90 | {
91 | "email": "alsk@alsk.com",
92 | "password": "618df746279402e73a79cf75c5723c556bb5e7db62083d88448b4995da0ed915fed42dbd7a7b6cd268ecaeb479abce4a951ed5e858fc4d3d7da3b61d9f0b5138.bbc49cf62675c387",
93 | "id": "7b4d7485"
94 | },
95 | {
96 | "email": "alskdfj@alskjdf.com",
97 | "password": "502670212f115561ff637096e984e425f1a5cc33748053f08aaf52a34430743d40158ee28206dd9813fc9fc81bd26a8a54e515c606ac3f97d20e9f1085bcbb9e.c34e6f08e5943cbd",
98 | "id": "f68a3efe"
99 | }
100 | ]
--------------------------------------------------------------------------------
/ecomm/views/admin/auth/signin.js:
--------------------------------------------------------------------------------
1 | const layout = require('../layout');
2 | const { getError } = require('../../helpers');
3 |
4 | module.exports = ({ errors }) => {
5 | return layout({
6 | content: `
7 |
28 | `
29 | });
30 | };
31 |
--------------------------------------------------------------------------------
/ecomm/views/admin/auth/signup.js:
--------------------------------------------------------------------------------
1 | const layout = require('../layout');
2 | const { getError } = require('../../helpers');
3 |
4 | module.exports = ({ req, errors }) => {
5 | console.log(errors);
6 | return layout({
7 | content: `
8 |
37 | `
38 | });
39 | };
40 |
--------------------------------------------------------------------------------
/ecomm/views/admin/layout.js:
--------------------------------------------------------------------------------
1 | module.exports = ({ content }) => {
2 | return `
3 |
4 |
5 |
6 |
7 |
8 | Shop
9 |
10 |
11 |
12 |
13 |
14 |
15 |
33 |
34 | ${content}
35 |
36 |
37 |
38 | `;
39 | };
40 |
--------------------------------------------------------------------------------
/ecomm/views/admin/products/edit.js:
--------------------------------------------------------------------------------
1 | const layout = require('../layout');
2 | const { getError } = require('../../helpers');
3 |
4 | module.exports = ({ product, errors }) => {
5 | return layout({
6 | content: `
7 |
8 |
9 |
Edit a Product
10 |
11 |
35 |
36 |
37 | `
38 | });
39 | };
40 |
--------------------------------------------------------------------------------
/ecomm/views/admin/products/index.js:
--------------------------------------------------------------------------------
1 | const layout = require('../layout');
2 |
3 | module.exports = ({ products }) => {
4 | const renderedProducts = products
5 | .map(product => {
6 | return `
7 |
8 | ${product.title} |
9 | ${product.price} |
10 |
11 |
12 |
15 |
16 | |
17 |
18 |
21 | |
22 |
23 | `;
24 | })
25 | .join('');
26 |
27 | return layout({
28 | content: `
29 |
33 |
34 |
35 |
36 | Title |
37 | Price |
38 | Edit |
39 | Delete |
40 |
41 |
42 |
43 | ${renderedProducts}
44 |
45 |
46 | `
47 | });
48 | };
49 |
--------------------------------------------------------------------------------
/ecomm/views/admin/products/new.js:
--------------------------------------------------------------------------------
1 | const layout = require('../layout');
2 | const { getError } = require('../../helpers');
3 |
4 | module.exports = ({ errors }) => {
5 | return layout({
6 | content: `
7 |
8 |
9 |
Create a Product
10 |
11 |
31 |
32 |
33 | `
34 | });
35 | };
36 |
--------------------------------------------------------------------------------
/ecomm/views/carts/show.js:
--------------------------------------------------------------------------------
1 | const layout = require('../layout');
2 |
3 | module.exports = ({ items }) => {
4 | // let totalPrice = 0;
5 | // for (let item of items) {
6 | // totalPrice += item.quantity * item.product.price;
7 | // }
8 |
9 | const totalPrice = items.reduce((prev, item) => {
10 | return prev + item.quantity * item.product.price;
11 | }, 0);
12 |
13 | const renderedItems = items
14 | .map(item => {
15 | return `
16 |
17 |
${item.product.title}
18 |
19 |
20 | $${item.product.price} X ${item.quantity} =
21 |
22 |
23 | $${item.product.price * item.quantity}
24 |
25 |
26 |
34 |
35 |
36 |
37 | `;
38 | })
39 | .join('');
40 |
41 | return layout({
42 | content: `
43 |
44 |
45 |
46 |
47 |
Shopping Cart
48 |
49 | ${renderedItems}
50 |
51 |
52 |
55 |
$${totalPrice}
56 |
57 |
58 |
59 |
60 |
61 |
62 | `
63 | });
64 | };
65 |
--------------------------------------------------------------------------------
/ecomm/views/helpers.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | getError(errors, prop) {
3 | try {
4 | return errors.mapped()[prop].msg;
5 | } catch (err) {
6 | return '';
7 | }
8 | }
9 | };
10 |
--------------------------------------------------------------------------------
/ecomm/views/layout.js:
--------------------------------------------------------------------------------
1 | module.exports = ({ content }) => {
2 | return `
3 |
4 |
5 |
6 |
7 |
8 | Shop
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
40 |
59 |
60 |
61 | ${content}
62 |
63 |
64 | `;
65 | };
66 |
--------------------------------------------------------------------------------
/ecomm/views/products/index.js:
--------------------------------------------------------------------------------
1 | const layout = require('../layout');
2 |
3 | module.exports = ({ products }) => {
4 | const renderedProducts = products
5 | .map(product => {
6 | return `
7 |
8 |
9 |
10 |
11 |
12 |
13 |
${product.title}
14 | $${product.price}
15 |
16 |
24 |
25 |
26 | `;
27 | })
28 | .join('\n');
29 |
30 | return layout({
31 | content: `
32 |
33 |
34 |
35 |

36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
Featured Items
47 |
48 | ${renderedProducts}
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 | `
57 | });
58 | };
59 |
--------------------------------------------------------------------------------
/hidash/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | forEach(arr, fn) {
3 | // for (let i = 0; i < arr.length; i++) {
4 | // const value = arr[i];
5 | // fn(value, i);
6 | // }
7 |
8 | for (let index in arr) {
9 | fn(arr[index], index);
10 | }
11 | },
12 | map(arr, fn) {
13 | const result = [];
14 |
15 | for (let i = 0; i < arr.length; i++) {
16 | result.push(fn(arr[i], i));
17 | }
18 |
19 | return result;
20 | }
21 | };
22 |
--------------------------------------------------------------------------------
/hidash/index.test.js:
--------------------------------------------------------------------------------
1 | const assert = require('assert');
2 | const { forEach, map } = require('./index');
3 |
4 | it('The forEach function', () => {
5 | let sum = 0;
6 | forEach([1, 2, 3], value => {
7 | sum += value;
8 | });
9 |
10 | assert.strictEqual(sum, 6, 'Expected forEach to sum the array');
11 | });
12 |
13 | it('The map function', () => {
14 | const result = map([1, 2, 3], value => {
15 | return value * 2;
16 | });
17 |
18 | assert.deepStrictEqual(result, [2, 4, 6]);
19 | });
20 |
--------------------------------------------------------------------------------
/list/index.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | const fs = require('fs');
4 | const util = require('util');
5 | const chalk = require('chalk');
6 | const path = require('path');
7 |
8 | // Method #2
9 | // const lstat = util.promisify(fs.lstat);
10 |
11 | // Method #3
12 | const { lstat } = fs.promises;
13 |
14 | const targetDir = process.argv[2] || process.cwd();
15 |
16 | fs.readdir(targetDir, async (err, filenames) => {
17 | if (err) {
18 | console.log(err);
19 | }
20 |
21 | const statPromises = filenames.map(filename => {
22 | return lstat(path.join(targetDir, filename));
23 | });
24 |
25 | const allStats = await Promise.all(statPromises);
26 |
27 | for (let stats of allStats) {
28 | const index = allStats.indexOf(stats);
29 |
30 | if (stats.isFile()) {
31 | console.log(filenames[index]);
32 | } else {
33 | console.log(chalk.bold(filenames[index]));
34 | }
35 | }
36 | });
37 |
38 | // Method #1
39 | // const lstat = filename => {
40 | // return new Promise((resolve, reject) => {
41 | // fs.lstat(filename, (err, stats) => {
42 | // if (err) {
43 | // reject(err);
44 | // }
45 |
46 | // resolve(stats);
47 | // });
48 | // });
49 | // };
50 |
--------------------------------------------------------------------------------
/list/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "list",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "ansi-styles": {
8 | "version": "3.2.1",
9 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
10 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
11 | "requires": {
12 | "color-convert": "^1.9.0"
13 | }
14 | },
15 | "chalk": {
16 | "version": "2.4.2",
17 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
18 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
19 | "requires": {
20 | "ansi-styles": "^3.2.1",
21 | "escape-string-regexp": "^1.0.5",
22 | "supports-color": "^5.3.0"
23 | }
24 | },
25 | "color-convert": {
26 | "version": "1.9.3",
27 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
28 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
29 | "requires": {
30 | "color-name": "1.1.3"
31 | }
32 | },
33 | "color-name": {
34 | "version": "1.1.3",
35 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
36 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
37 | },
38 | "escape-string-regexp": {
39 | "version": "1.0.5",
40 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
41 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
42 | },
43 | "has-flag": {
44 | "version": "3.0.0",
45 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
46 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
47 | },
48 | "supports-color": {
49 | "version": "5.5.0",
50 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
51 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
52 | "requires": {
53 | "has-flag": "^3.0.0"
54 | }
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/list/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "list",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "bin": {
13 | "nls": "index.js"
14 | },
15 | "dependencies": {
16 | "chalk": "^2.4.2"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/list/test.js:
--------------------------------------------------------------------------------
1 | console.log('hi how are you?');
2 |
--------------------------------------------------------------------------------
/maze/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
29 |
30 |
31 |
32 |
You Win!
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/maze/index.js:
--------------------------------------------------------------------------------
1 | const { Engine, Render, Runner, World, Bodies, Body, Events } = Matter;
2 |
3 | const cellsHorizontal = 16;
4 | const cellsVertical = 10;
5 | const width = window.innerWidth;
6 | const height = window.innerHeight;
7 |
8 | const unitLengthX = width / cellsHorizontal;
9 | const unitLengthY = height / cellsVertical;
10 |
11 | const engine = Engine.create();
12 | engine.world.gravity.y = 0;
13 | const { world } = engine;
14 | const render = Render.create({
15 | element: document.body,
16 | engine: engine,
17 | options: {
18 | wireframes: false,
19 | width,
20 | height
21 | }
22 | });
23 | Render.run(render);
24 | Runner.run(Runner.create(), engine);
25 |
26 | // Walls
27 | const walls = [
28 | Bodies.rectangle(width / 2, 0, width, 2, { isStatic: true }),
29 | Bodies.rectangle(width / 2, height, width, 2, { isStatic: true }),
30 | Bodies.rectangle(0, height / 2, 2, height, { isStatic: true }),
31 | Bodies.rectangle(width, height / 2, 2, height, { isStatic: true })
32 | ];
33 | World.add(world, walls);
34 |
35 | // Maze generation
36 |
37 | const shuffle = arr => {
38 | let counter = arr.length;
39 |
40 | while (counter > 0) {
41 | const index = Math.floor(Math.random() * counter);
42 |
43 | counter--;
44 |
45 | const temp = arr[counter];
46 | arr[counter] = arr[index];
47 | arr[index] = temp;
48 | }
49 |
50 | return arr;
51 | };
52 |
53 | const grid = Array(cellsVertical)
54 | .fill(null)
55 | .map(() => Array(cellsHorizontal).fill(false));
56 |
57 | const verticals = Array(cellsVertical)
58 | .fill(null)
59 | .map(() => Array(cellsHorizontal - 1).fill(false));
60 |
61 | const horizontals = Array(cellsVertical - 1)
62 | .fill(null)
63 | .map(() => Array(cellsHorizontal).fill(false));
64 |
65 | const startRow = Math.floor(Math.random() * cellsVertical);
66 | const startColumn = Math.floor(Math.random() * cellsHorizontal);
67 |
68 | const stepThroughCell = (row, column) => {
69 | // If i have visted the cell at [row, column], then return
70 | if (grid[row][column]) {
71 | return;
72 | }
73 |
74 | // Mark this cell as being visited
75 | grid[row][column] = true;
76 |
77 | // Assemble randomly-ordered list of neighbors
78 | const neighbors = shuffle([
79 | [row - 1, column, 'up'],
80 | [row, column + 1, 'right'],
81 | [row + 1, column, 'down'],
82 | [row, column - 1, 'left']
83 | ]);
84 | // For each neighbor....
85 | for (let neighbor of neighbors) {
86 | const [nextRow, nextColumn, direction] = neighbor;
87 |
88 | // See if that neighbor is out of bounds
89 | if (
90 | nextRow < 0 ||
91 | nextRow >= cellsVertical ||
92 | nextColumn < 0 ||
93 | nextColumn >= cellsHorizontal
94 | ) {
95 | continue;
96 | }
97 |
98 | // If we have visited that neighbor, continue to next neighbor
99 | if (grid[nextRow][nextColumn]) {
100 | continue;
101 | }
102 |
103 | // Remove a wall from either horizontals or verticals
104 | if (direction === 'left') {
105 | verticals[row][column - 1] = true;
106 | } else if (direction === 'right') {
107 | verticals[row][column] = true;
108 | } else if (direction === 'up') {
109 | horizontals[row - 1][column] = true;
110 | } else if (direction === 'down') {
111 | horizontals[row][column] = true;
112 | }
113 |
114 | stepThroughCell(nextRow, nextColumn);
115 | }
116 | };
117 |
118 | stepThroughCell(startRow, startColumn);
119 |
120 | horizontals.forEach((row, rowIndex) => {
121 | row.forEach((open, columnIndex) => {
122 | if (open) {
123 | return;
124 | }
125 |
126 | const wall = Bodies.rectangle(
127 | columnIndex * unitLengthX + unitLengthX / 2,
128 | rowIndex * unitLengthY + unitLengthY,
129 | unitLengthX,
130 | 5,
131 | {
132 | label: 'wall',
133 | isStatic: true,
134 | render: {
135 | fillStyle: 'red'
136 | }
137 | }
138 | );
139 | World.add(world, wall);
140 | });
141 | });
142 |
143 | verticals.forEach((row, rowIndex) => {
144 | row.forEach((open, columnIndex) => {
145 | if (open) {
146 | return;
147 | }
148 |
149 | const wall = Bodies.rectangle(
150 | columnIndex * unitLengthX + unitLengthX,
151 | rowIndex * unitLengthY + unitLengthY / 2,
152 | 5,
153 | unitLengthY,
154 | {
155 | label: 'wall',
156 | isStatic: true,
157 | render: {
158 | fillStyle: 'red'
159 | }
160 | }
161 | );
162 | World.add(world, wall);
163 | });
164 | });
165 |
166 | // Goal
167 |
168 | const goal = Bodies.rectangle(
169 | width - unitLengthX / 2,
170 | height - unitLengthY / 2,
171 | unitLengthX * 0.7,
172 | unitLengthY * 0.7,
173 | {
174 | label: 'goal',
175 | isStatic: true,
176 | render: {
177 | fillStyle: 'green'
178 | }
179 | }
180 | );
181 | World.add(world, goal);
182 |
183 | // Ball
184 |
185 | const ballRadius = Math.min(unitLengthX, unitLengthY) / 4;
186 | const ball = Bodies.circle(unitLengthX / 2, unitLengthY / 2, ballRadius, {
187 | label: 'ball',
188 | render: {
189 | fillStyle: 'blue'
190 | }
191 | });
192 | World.add(world, ball);
193 |
194 | document.addEventListener('keydown', event => {
195 | const { x, y } = ball.velocity;
196 |
197 | if (event.keyCode === 87) {
198 | Body.setVelocity(ball, { x, y: y - 5 });
199 | }
200 |
201 | if (event.keyCode === 68) {
202 | Body.setVelocity(ball, { x: x + 5, y });
203 | }
204 |
205 | if (event.keyCode === 83) {
206 | Body.setVelocity(ball, { x, y: y + 5 });
207 | }
208 |
209 | if (event.keyCode === 65) {
210 | Body.setVelocity(ball, { x: x - 5, y });
211 | }
212 | });
213 |
214 | // Win Condition
215 |
216 | Events.on(engine, 'collisionStart', event => {
217 | event.pairs.forEach(collision => {
218 | const labels = ['ball', 'goal'];
219 |
220 | if (
221 | labels.includes(collision.bodyA.label) &&
222 | labels.includes(collision.bodyB.label)
223 | ) {
224 | document.querySelector('.winner').classList.remove('hidden');
225 | world.gravity.y = 1;
226 | world.bodies.forEach(body => {
227 | if (body.label === 'wall') {
228 | Body.setStatic(body, false);
229 | }
230 | });
231 | }
232 | });
233 | });
234 |
--------------------------------------------------------------------------------
/message/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
17 |
18 |
19 |
20 |
21 |
22 |
28 |
29 |
30 |
35 |
36 |
37 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/message/index.js:
--------------------------------------------------------------------------------
1 | const { hash } = window.location;
2 |
3 | const message = atob(hash.replace('#', ''));
4 |
5 | if (message) {
6 | document.querySelector('#message-form').classList.add('hide');
7 | document.querySelector('#message-show').classList.remove('hide');
8 |
9 | document.querySelector('h1').innerHTML = message;
10 | }
11 |
12 | document.querySelector('form').addEventListener('submit', event => {
13 | event.preventDefault();
14 |
15 | document.querySelector('#message-form').classList.add('hide');
16 | document.querySelector('#link-form').classList.remove('hide');
17 |
18 | const input = document.querySelector('#message-input');
19 | const encrypted = btoa(input.value);
20 |
21 | const linkInput = document.querySelector('#link-input');
22 | linkInput.value = `${window.location}#${encrypted}`;
23 | linkInput.select();
24 | });
25 |
--------------------------------------------------------------------------------
/movies-testing/autocomplete.js:
--------------------------------------------------------------------------------
1 | const createAutoComplete = ({
2 | root,
3 | renderOption,
4 | onOptionSelect,
5 | inputValue,
6 | fetchData
7 | }) => {
8 | root.innerHTML = `
9 |
10 |
11 |
12 |
15 |
16 | `;
17 |
18 | const input = root.querySelector('input');
19 | const dropdown = root.querySelector('.dropdown');
20 | const resultsWrapper = root.querySelector('.results');
21 |
22 | const onInput = async event => {
23 | const items = await fetchData(event.target.value);
24 |
25 | if (!items.length) {
26 | dropdown.classList.remove('is-active');
27 | return;
28 | }
29 |
30 | resultsWrapper.innerHTML = '';
31 | dropdown.classList.add('is-active');
32 | for (let item of items) {
33 | const option = document.createElement('a');
34 |
35 | option.classList.add('dropdown-item');
36 | option.innerHTML = renderOption(item);
37 | option.addEventListener('click', () => {
38 | dropdown.classList.remove('is-active');
39 | input.value = inputValue(item);
40 | onOptionSelect(item);
41 | });
42 |
43 | resultsWrapper.appendChild(option);
44 | }
45 | };
46 | input.addEventListener('input', debounce(onInput, 500));
47 |
48 | document.addEventListener('click', event => {
49 | if (!root.contains(event.target)) {
50 | dropdown.classList.remove('is-active');
51 | }
52 | });
53 | };
54 |
--------------------------------------------------------------------------------
/movies-testing/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | Movie Fight
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
41 |
42 |
43 |
Search For a Movie on Both Sides
44 |
We will tell you which is best!
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/movies-testing/index.js:
--------------------------------------------------------------------------------
1 | const autoCompleteConfig = {
2 | renderOption(movie) {
3 | const imgSrc = movie.Poster === 'N/A' ? '' : movie.Poster;
4 | return `
5 |
6 | ${movie.Title} (${movie.Year})
7 | `;
8 | },
9 | inputValue(movie) {
10 | return movie.Title;
11 | },
12 | async fetchData(searchTerm) {
13 | const response = await axios.get('http://www.omdbapi.com/', {
14 | params: {
15 | apikey: 'd9835cc5',
16 | s: searchTerm
17 | }
18 | });
19 |
20 | if (response.data.Error) {
21 | return [];
22 | }
23 |
24 | return response.data.Search;
25 | }
26 | };
27 |
28 | createAutoComplete({
29 | ...autoCompleteConfig,
30 | root: document.querySelector('#left-autocomplete'),
31 | onOptionSelect(movie) {
32 | document.querySelector('.tutorial').classList.add('is-hidden');
33 | onMovieSelect(movie, document.querySelector('#left-summary'), 'left');
34 | }
35 | });
36 | createAutoComplete({
37 | ...autoCompleteConfig,
38 | root: document.querySelector('#right-autocomplete'),
39 | onOptionSelect(movie) {
40 | document.querySelector('.tutorial').classList.add('is-hidden');
41 | onMovieSelect(movie, document.querySelector('#right-summary'), 'right');
42 | }
43 | });
44 |
45 | let leftMovie;
46 | let rightMovie;
47 | const onMovieSelect = async (movie, summaryElement, side) => {
48 | const response = await axios.get('http://www.omdbapi.com/', {
49 | params: {
50 | apikey: 'd9835cc5',
51 | i: movie.imdbID
52 | }
53 | });
54 |
55 | summaryElement.innerHTML = movieTemplate(response.data);
56 |
57 | if (side === 'left') {
58 | leftMovie = response.data;
59 | } else {
60 | rightMovie = response.data;
61 | }
62 |
63 | if (leftMovie && rightMovie) {
64 | runComparison();
65 | }
66 | };
67 |
68 | const runComparison = () => {
69 | const leftSideStats = document.querySelectorAll(
70 | '#left-summary .notification'
71 | );
72 | const rightSideStats = document.querySelectorAll(
73 | '#right-summary .notification'
74 | );
75 |
76 | leftSideStats.forEach((leftStat, index) => {
77 | const rightStat = rightSideStats[index];
78 |
79 | const leftSideValue = leftStat.dataset.value;
80 | const rightSideValue = rightStat.dataset.value;
81 |
82 | if (rightSideValue > leftSideValue) {
83 | leftStat.classList.remove('is-primary');
84 | leftStat.classList.add('is-warning');
85 | } else {
86 | rightStat.classList.remove('is-primary');
87 | rightStat.classList.add('is-warning');
88 | }
89 | });
90 | };
91 |
92 | const movieTemplate = movieDetail => {
93 | const dollars = parseInt(
94 | movieDetail.BoxOffice.replace(/\$/g, '').replace(/,/g, '')
95 | );
96 | const metascore = parseInt(movieDetail.Metascore);
97 | const imdbRating = parseFloat(movieDetail.imdbRating);
98 | const imdbVotes = parseInt(movieDetail.imdbVotes.replace(/,/g, ''));
99 | const awards = movieDetail.Awards.split(' ').reduce((prev, word) => {
100 | const value = parseInt(word);
101 |
102 | if (isNaN(value)) {
103 | return prev;
104 | } else {
105 | return prev + value;
106 | }
107 | }, 0);
108 |
109 | return `
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
${movieDetail.Title}
119 |
${movieDetail.Genre}
120 |
${movieDetail.Plot}
121 |
122 |
123 |
124 |
125 |
126 | ${movieDetail.Awards}
127 | Awards
128 |
129 |
130 | ${movieDetail.BoxOffice}
131 | Box Office
132 |
133 |
134 | ${movieDetail.Metascore}
135 | Metascore
136 |
137 |
138 | ${movieDetail.imdbRating}
139 | IMDB Rating
140 |
141 |
142 | ${movieDetail.imdbVotes}
143 | IMDB Votes
144 |
145 | `;
146 | };
147 |
--------------------------------------------------------------------------------
/movies-testing/style.css:
--------------------------------------------------------------------------------
1 | .tutorial {
2 | width: 600px;
3 | text-align: center;
4 | margin: auto;
5 | }
6 |
7 | figure.image {
8 | display: flex;
9 | justify-content: center;
10 | background-color: black;
11 | }
12 |
13 | figure .image img {
14 | width: 128px;
15 | }
16 |
17 | .notification {
18 | margin-top: 20px !important;
19 | }
20 |
21 | .title .icon {
22 | margin-left: 15px;
23 | }
24 |
25 | .hero {
26 | margin-bottom: 20px;
27 | }
28 |
29 | .forms {
30 | display: flex;
31 | justify-content: space-around;
32 | }
33 |
34 | .results {
35 | max-height: 500px;
36 | overflow-y: scroll;
37 | }
38 |
39 | .dropdown-item {
40 | display: flex;
41 | align-items: center;
42 | height: 60px;
43 | }
44 |
45 | .dropdown-item img {
46 | height: 50px;
47 | margin-right: 10px;
48 | }
49 |
--------------------------------------------------------------------------------
/movies-testing/test/autocomplete.test.js:
--------------------------------------------------------------------------------
1 | const waitFor = selector => {
2 | return new Promise((resolve, reject) => {
3 | const interval = setInterval(() => {
4 | if (document.querySelector(selector)) {
5 | clearInterval(interval);
6 | clearTimeout(timeout);
7 | resolve();
8 | }
9 | }, 30);
10 |
11 | const timeout = setTimeout(() => {
12 | clearInterval(interval);
13 | reject();
14 | }, 2000);
15 | });
16 | };
17 |
18 | beforeEach(() => {
19 | document.querySelector('#target').innerHTML = '';
20 | createAutoComplete({
21 | root: document.querySelector('#target'),
22 | fetchData() {
23 | return [
24 | { Title: 'Avengers' },
25 | { Title: 'Not Avengers' },
26 | { Title: 'Some other movie' }
27 | ];
28 | },
29 | renderOption(movie) {
30 | return movie.Title;
31 | }
32 | });
33 | });
34 |
35 | it('Dropdown starts closed', () => {
36 | const dropdown = document.querySelector('.dropdown');
37 |
38 | expect(dropdown.className).not.to.include('is-active');
39 | });
40 |
41 | it('After searching, dropdown opens up', async () => {
42 | const input = document.querySelector('input');
43 | input.value = 'avengers';
44 | input.dispatchEvent(new Event('input'));
45 |
46 | await waitFor('.dropdown-item');
47 |
48 | const dropdown = document.querySelector('.dropdown');
49 | expect(dropdown.className).to.include('is-active');
50 | });
51 |
52 | it('After searching, displays some results', async () => {
53 | const input = document.querySelector('input');
54 | input.value = 'avengers';
55 | input.dispatchEvent(new Event('input'));
56 |
57 | await waitFor('.dropdown-item');
58 |
59 | const items = document.querySelectorAll('.dropdown-item');
60 |
61 | expect(items.length).to.equal(3);
62 | });
63 |
--------------------------------------------------------------------------------
/movies-testing/test/test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Mocha Tests
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
23 |
24 |
25 |
26 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/movies-testing/utils.js:
--------------------------------------------------------------------------------
1 | const debounce = (func, delay = 1000) => {
2 | let timeoutId;
3 | return (...args) => {
4 | if (timeoutId) {
5 | clearTimeout(timeoutId);
6 | }
7 | timeoutId = setTimeout(() => {
8 | func.apply(null, args);
9 | }, delay);
10 | };
11 | };
12 |
--------------------------------------------------------------------------------
/movies/autocomplete.js:
--------------------------------------------------------------------------------
1 | const createAutoComplete = ({
2 | root,
3 | renderOption,
4 | onOptionSelect,
5 | inputValue,
6 | fetchData
7 | }) => {
8 | root.innerHTML = `
9 |
10 |
11 |
12 |
15 |
16 | `;
17 |
18 | const input = root.querySelector('input');
19 | const dropdown = root.querySelector('.dropdown');
20 | const resultsWrapper = root.querySelector('.results');
21 |
22 | const onInput = async event => {
23 | const items = await fetchData(event.target.value);
24 |
25 | if (!items.length) {
26 | dropdown.classList.remove('is-active');
27 | return;
28 | }
29 |
30 | resultsWrapper.innerHTML = '';
31 | dropdown.classList.add('is-active');
32 | for (let item of items) {
33 | const option = document.createElement('a');
34 |
35 | option.classList.add('dropdown-item');
36 | option.innerHTML = renderOption(item);
37 | option.addEventListener('click', () => {
38 | dropdown.classList.remove('is-active');
39 | input.value = inputValue(item);
40 | onOptionSelect(item);
41 | });
42 |
43 | resultsWrapper.appendChild(option);
44 | }
45 | };
46 | input.addEventListener('input', debounce(onInput, 500));
47 |
48 | document.addEventListener('click', event => {
49 | if (!root.contains(event.target)) {
50 | dropdown.classList.remove('is-active');
51 | }
52 | });
53 | };
54 |
--------------------------------------------------------------------------------
/movies/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | Movie Fight
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
41 |
42 |
43 |
Search For a Movie on Both Sides
44 |
We will tell you which is best!
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/movies/index.js:
--------------------------------------------------------------------------------
1 | const autoCompleteConfig = {
2 | renderOption(movie) {
3 | const imgSrc = movie.Poster === 'N/A' ? '' : movie.Poster;
4 | return `
5 |
6 | ${movie.Title} (${movie.Year})
7 | `;
8 | },
9 | inputValue(movie) {
10 | return movie.Title;
11 | },
12 | async fetchData(searchTerm) {
13 | const response = await axios.get('http://www.omdbapi.com/', {
14 | params: {
15 | apikey: 'd9835cc5',
16 | s: searchTerm
17 | }
18 | });
19 |
20 | if (response.data.Error) {
21 | return [];
22 | }
23 |
24 | return response.data.Search;
25 | }
26 | };
27 |
28 | createAutoComplete({
29 | ...autoCompleteConfig,
30 | root: document.querySelector('#left-autocomplete'),
31 | onOptionSelect(movie) {
32 | document.querySelector('.tutorial').classList.add('is-hidden');
33 | onMovieSelect(movie, document.querySelector('#left-summary'), 'left');
34 | }
35 | });
36 | createAutoComplete({
37 | ...autoCompleteConfig,
38 | root: document.querySelector('#right-autocomplete'),
39 | onOptionSelect(movie) {
40 | document.querySelector('.tutorial').classList.add('is-hidden');
41 | onMovieSelect(movie, document.querySelector('#right-summary'), 'right');
42 | }
43 | });
44 |
45 | let leftMovie;
46 | let rightMovie;
47 | const onMovieSelect = async (movie, summaryElement, side) => {
48 | const response = await axios.get('http://www.omdbapi.com/', {
49 | params: {
50 | apikey: 'd9835cc5',
51 | i: movie.imdbID
52 | }
53 | });
54 |
55 | summaryElement.innerHTML = movieTemplate(response.data);
56 |
57 | if (side === 'left') {
58 | leftMovie = response.data;
59 | } else {
60 | rightMovie = response.data;
61 | }
62 |
63 | if (leftMovie && rightMovie) {
64 | runComparison();
65 | }
66 | };
67 |
68 | const runComparison = () => {
69 | const leftSideStats = document.querySelectorAll(
70 | '#left-summary .notification'
71 | );
72 | const rightSideStats = document.querySelectorAll(
73 | '#right-summary .notification'
74 | );
75 |
76 | leftSideStats.forEach((leftStat, index) => {
77 | const rightStat = rightSideStats[index];
78 |
79 | const leftSideValue = leftStat.dataset.value;
80 | const rightSideValue = rightStat.dataset.value;
81 |
82 | if (rightSideValue > leftSideValue) {
83 | leftStat.classList.remove('is-primary');
84 | leftStat.classList.add('is-warning');
85 | } else {
86 | rightStat.classList.remove('is-primary');
87 | rightStat.classList.add('is-warning');
88 | }
89 | });
90 | };
91 |
92 | const movieTemplate = movieDetail => {
93 | const dollars = parseInt(
94 | movieDetail.BoxOffice.replace(/\$/g, '').replace(/,/g, '')
95 | );
96 | const metascore = parseInt(movieDetail.Metascore);
97 | const imdbRating = parseFloat(movieDetail.imdbRating);
98 | const imdbVotes = parseInt(movieDetail.imdbVotes.replace(/,/g, ''));
99 | const awards = movieDetail.Awards.split(' ').reduce((prev, word) => {
100 | const value = parseInt(word);
101 |
102 | if (isNaN(value)) {
103 | return prev;
104 | } else {
105 | return prev + value;
106 | }
107 | }, 0);
108 |
109 | return `
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
${movieDetail.Title}
119 |
${movieDetail.Genre}
120 |
${movieDetail.Plot}
121 |
122 |
123 |
124 |
125 |
126 | ${movieDetail.Awards}
127 | Awards
128 |
129 |
130 | ${movieDetail.BoxOffice}
131 | Box Office
132 |
133 |
134 | ${movieDetail.Metascore}
135 | Metascore
136 |
137 |
138 | ${movieDetail.imdbRating}
139 | IMDB Rating
140 |
141 |
142 | ${movieDetail.imdbVotes}
143 | IMDB Votes
144 |
145 | `;
146 | };
147 |
--------------------------------------------------------------------------------
/movies/style.css:
--------------------------------------------------------------------------------
1 | .tutorial {
2 | width: 600px;
3 | text-align: center;
4 | margin: auto;
5 | }
6 |
7 | figure.image {
8 | display: flex;
9 | justify-content: center;
10 | background-color: black;
11 | }
12 |
13 | figure .image img {
14 | width: 128px;
15 | }
16 |
17 | .notification {
18 | margin-top: 20px !important;
19 | }
20 |
21 | .title .icon {
22 | margin-left: 15px;
23 | }
24 |
25 | .hero {
26 | margin-bottom: 20px;
27 | }
28 |
29 | .forms {
30 | display: flex;
31 | justify-content: space-around;
32 | }
33 |
34 | .results {
35 | max-height: 500px;
36 | overflow-y: scroll;
37 | }
38 |
39 | .dropdown-item {
40 | display: flex;
41 | align-items: center;
42 | height: 60px;
43 | }
44 |
45 | .dropdown-item img {
46 | height: 50px;
47 | margin-right: 10px;
48 | }
49 |
--------------------------------------------------------------------------------
/movies/utils.js:
--------------------------------------------------------------------------------
1 | const debounce = (func, delay = 1000) => {
2 | let timeoutId;
3 | return (...args) => {
4 | if (timeoutId) {
5 | clearTimeout(timeoutId);
6 | }
7 | timeoutId = setTimeout(() => {
8 | func.apply(null, args);
9 | }, delay);
10 | };
11 | };
12 |
--------------------------------------------------------------------------------
/timer/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/timer/index.js:
--------------------------------------------------------------------------------
1 | const durationInput = document.querySelector('#duration');
2 | const startButton = document.querySelector('#start');
3 | const pauseButton = document.querySelector('#pause');
4 | const circle = document.querySelector('circle');
5 |
6 | const perimeter = circle.getAttribute('r') * 2 * Math.PI;
7 | circle.setAttribute('stroke-dasharray', perimeter);
8 |
9 | let duration;
10 | const timer = new Timer(durationInput, startButton, pauseButton, {
11 | onStart(totalDuration) {
12 | duration = totalDuration;
13 | },
14 | onTick(timeRemaining) {
15 | circle.setAttribute(
16 | 'stroke-dashoffset',
17 | (perimeter * timeRemaining) / duration - perimeter
18 | );
19 | },
20 | onComplete() {
21 | console.log('Timer is completed');
22 | }
23 | });
24 |
--------------------------------------------------------------------------------
/timer/style.css:
--------------------------------------------------------------------------------
1 | .dial {
2 | height: 400px;
3 | width: 400px;
4 | }
5 |
6 | .timer {
7 | position: relative;
8 | display: inline-block;
9 | }
10 |
11 | * {
12 | font: inherit;
13 | }
14 |
15 | body {
16 | font-family: 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Fira Sans',
17 | 'Droid Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
18 | }
19 |
20 | .timer input {
21 | display: block;
22 | border: none;
23 | width: 240px;
24 | font-size: 90px;
25 | text-align: center;
26 | }
27 |
28 | .timer button {
29 | border: none;
30 | font-size: 36px;
31 | cursor: pointer;
32 | }
33 |
34 | .timer button:focus {
35 | outline: none;
36 | }
37 |
38 | .timer input:focus {
39 | outline: none;
40 | }
41 |
42 | body {
43 | display: flex;
44 | justify-content: center;
45 | align-items: center;
46 | height: 100vh;
47 | }
48 |
49 | .controls {
50 | position: absolute;
51 | top: 0;
52 | left: 0;
53 | right: 0;
54 | bottom: 0;
55 | display: flex;
56 | justify-content: center;
57 | align-items: center;
58 | flex-direction: column;
59 | }
60 |
--------------------------------------------------------------------------------
/timer/timer.js:
--------------------------------------------------------------------------------
1 | class Timer {
2 | constructor(durationInput, startButton, pauseButton, callbacks) {
3 | this.durationInput = durationInput;
4 | this.startButton = startButton;
5 | this.pauseButton = pauseButton;
6 | if (callbacks) {
7 | this.onStart = callbacks.onStart;
8 | this.onTick = callbacks.onTick;
9 | this.onComplete = callbacks.onComplete;
10 | }
11 |
12 | this.startButton.addEventListener('click', this.start);
13 | this.pauseButton.addEventListener('click', this.pause);
14 | }
15 |
16 | start = () => {
17 | if (this.onStart) {
18 | this.onStart(this.timeRemaining);
19 | }
20 | this.tick();
21 | this.interval = setInterval(this.tick, 20);
22 | };
23 |
24 | pause = () => {
25 | clearInterval(this.interval);
26 | };
27 |
28 | tick = () => {
29 | if (this.timeRemaining <= 0) {
30 | this.pause();
31 | if (this.onComplete) {
32 | this.onComplete();
33 | }
34 | } else {
35 | this.timeRemaining = this.timeRemaining - 0.02;
36 | if (this.onTick) {
37 | this.onTick(this.timeRemaining);
38 | }
39 | }
40 | };
41 |
42 | get timeRemaining() {
43 | return parseFloat(this.durationInput.value);
44 | }
45 |
46 | set timeRemaining(time) {
47 | this.durationInput.value = time.toFixed(2);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/tme/index.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | const Runner = require('./runner');
4 | const runner = new Runner();
5 |
6 | const run = async () => {
7 | await runner.collectFiles(process.cwd());
8 | runner.runTests();
9 | };
10 |
11 | run();
12 |
--------------------------------------------------------------------------------
/tme/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tme",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "@types/color-name": {
8 | "version": "1.1.1",
9 | "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
10 | "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ=="
11 | },
12 | "abab": {
13 | "version": "2.0.3",
14 | "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.3.tgz",
15 | "integrity": "sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg=="
16 | },
17 | "acorn": {
18 | "version": "7.1.0",
19 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.0.tgz",
20 | "integrity": "sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ=="
21 | },
22 | "acorn-globals": {
23 | "version": "4.3.4",
24 | "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.4.tgz",
25 | "integrity": "sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==",
26 | "requires": {
27 | "acorn": "^6.0.1",
28 | "acorn-walk": "^6.0.1"
29 | },
30 | "dependencies": {
31 | "acorn": {
32 | "version": "6.4.0",
33 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.0.tgz",
34 | "integrity": "sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw=="
35 | }
36 | }
37 | },
38 | "acorn-walk": {
39 | "version": "6.2.0",
40 | "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz",
41 | "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA=="
42 | },
43 | "ajv": {
44 | "version": "6.10.2",
45 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz",
46 | "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==",
47 | "requires": {
48 | "fast-deep-equal": "^2.0.1",
49 | "fast-json-stable-stringify": "^2.0.0",
50 | "json-schema-traverse": "^0.4.1",
51 | "uri-js": "^4.2.2"
52 | }
53 | },
54 | "ansi-styles": {
55 | "version": "4.2.0",
56 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.0.tgz",
57 | "integrity": "sha512-7kFQgnEaMdRtwf6uSfUnVr9gSGC7faurn+J/Mv90/W+iTtN0405/nLdopfMWwchyxhbGYl6TC4Sccn9TUkGAgg==",
58 | "requires": {
59 | "@types/color-name": "^1.1.1",
60 | "color-convert": "^2.0.1"
61 | }
62 | },
63 | "array-equal": {
64 | "version": "1.0.0",
65 | "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz",
66 | "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM="
67 | },
68 | "asn1": {
69 | "version": "0.2.4",
70 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
71 | "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
72 | "requires": {
73 | "safer-buffer": "~2.1.0"
74 | }
75 | },
76 | "assert-plus": {
77 | "version": "1.0.0",
78 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
79 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
80 | },
81 | "async-limiter": {
82 | "version": "1.0.1",
83 | "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
84 | "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
85 | },
86 | "asynckit": {
87 | "version": "0.4.0",
88 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
89 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
90 | },
91 | "aws-sign2": {
92 | "version": "0.7.0",
93 | "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
94 | "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
95 | },
96 | "aws4": {
97 | "version": "1.9.0",
98 | "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.0.tgz",
99 | "integrity": "sha512-Uvq6hVe90D0B2WEnUqtdgY1bATGz3mw33nH9Y+dmA+w5DHvUmBgkr5rM/KCHpCsiFNRUfokW/szpPPgMK2hm4A=="
100 | },
101 | "bcrypt-pbkdf": {
102 | "version": "1.0.2",
103 | "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
104 | "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
105 | "requires": {
106 | "tweetnacl": "^0.14.3"
107 | }
108 | },
109 | "browser-process-hrtime": {
110 | "version": "0.1.3",
111 | "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz",
112 | "integrity": "sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw=="
113 | },
114 | "caseless": {
115 | "version": "0.12.0",
116 | "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
117 | "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
118 | },
119 | "chalk": {
120 | "version": "3.0.0",
121 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
122 | "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
123 | "requires": {
124 | "ansi-styles": "^4.1.0",
125 | "supports-color": "^7.1.0"
126 | }
127 | },
128 | "color-convert": {
129 | "version": "2.0.1",
130 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
131 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
132 | "requires": {
133 | "color-name": "~1.1.4"
134 | }
135 | },
136 | "color-name": {
137 | "version": "1.1.4",
138 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
139 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
140 | },
141 | "combined-stream": {
142 | "version": "1.0.8",
143 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
144 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
145 | "requires": {
146 | "delayed-stream": "~1.0.0"
147 | }
148 | },
149 | "core-util-is": {
150 | "version": "1.0.2",
151 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
152 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
153 | },
154 | "cssom": {
155 | "version": "0.4.4",
156 | "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz",
157 | "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw=="
158 | },
159 | "cssstyle": {
160 | "version": "2.0.0",
161 | "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.0.0.tgz",
162 | "integrity": "sha512-QXSAu2WBsSRXCPjvI43Y40m6fMevvyRm8JVAuF9ksQz5jha4pWP1wpaK7Yu5oLFc6+XAY+hj8YhefyXcBB53gg==",
163 | "requires": {
164 | "cssom": "~0.3.6"
165 | },
166 | "dependencies": {
167 | "cssom": {
168 | "version": "0.3.8",
169 | "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz",
170 | "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg=="
171 | }
172 | }
173 | },
174 | "dashdash": {
175 | "version": "1.14.1",
176 | "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
177 | "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
178 | "requires": {
179 | "assert-plus": "^1.0.0"
180 | }
181 | },
182 | "data-urls": {
183 | "version": "1.1.0",
184 | "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz",
185 | "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==",
186 | "requires": {
187 | "abab": "^2.0.0",
188 | "whatwg-mimetype": "^2.2.0",
189 | "whatwg-url": "^7.0.0"
190 | }
191 | },
192 | "deep-is": {
193 | "version": "0.1.3",
194 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
195 | "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ="
196 | },
197 | "delayed-stream": {
198 | "version": "1.0.0",
199 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
200 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
201 | },
202 | "domexception": {
203 | "version": "1.0.1",
204 | "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz",
205 | "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==",
206 | "requires": {
207 | "webidl-conversions": "^4.0.2"
208 | }
209 | },
210 | "ecc-jsbn": {
211 | "version": "0.1.2",
212 | "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
213 | "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
214 | "requires": {
215 | "jsbn": "~0.1.0",
216 | "safer-buffer": "^2.1.0"
217 | }
218 | },
219 | "escodegen": {
220 | "version": "1.12.0",
221 | "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.12.0.tgz",
222 | "integrity": "sha512-TuA+EhsanGcme5T3R0L80u4t8CpbXQjegRmf7+FPTJrtCTErXFeelblRgHQa1FofEzqYYJmJ/OqjTwREp9qgmg==",
223 | "requires": {
224 | "esprima": "^3.1.3",
225 | "estraverse": "^4.2.0",
226 | "esutils": "^2.0.2",
227 | "optionator": "^0.8.1",
228 | "source-map": "~0.6.1"
229 | }
230 | },
231 | "esprima": {
232 | "version": "3.1.3",
233 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz",
234 | "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM="
235 | },
236 | "estraverse": {
237 | "version": "4.3.0",
238 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
239 | "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="
240 | },
241 | "esutils": {
242 | "version": "2.0.3",
243 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
244 | "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="
245 | },
246 | "extend": {
247 | "version": "3.0.2",
248 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
249 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
250 | },
251 | "extsprintf": {
252 | "version": "1.3.0",
253 | "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
254 | "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
255 | },
256 | "fast-deep-equal": {
257 | "version": "2.0.1",
258 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
259 | "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk="
260 | },
261 | "fast-json-stable-stringify": {
262 | "version": "2.0.0",
263 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
264 | "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I="
265 | },
266 | "fast-levenshtein": {
267 | "version": "2.0.6",
268 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
269 | "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc="
270 | },
271 | "forever-agent": {
272 | "version": "0.6.1",
273 | "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
274 | "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
275 | },
276 | "form-data": {
277 | "version": "2.3.3",
278 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
279 | "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
280 | "requires": {
281 | "asynckit": "^0.4.0",
282 | "combined-stream": "^1.0.6",
283 | "mime-types": "^2.1.12"
284 | }
285 | },
286 | "getpass": {
287 | "version": "0.1.7",
288 | "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
289 | "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
290 | "requires": {
291 | "assert-plus": "^1.0.0"
292 | }
293 | },
294 | "har-schema": {
295 | "version": "2.0.0",
296 | "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
297 | "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
298 | },
299 | "har-validator": {
300 | "version": "5.1.3",
301 | "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
302 | "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
303 | "requires": {
304 | "ajv": "^6.5.5",
305 | "har-schema": "^2.0.0"
306 | }
307 | },
308 | "has-flag": {
309 | "version": "4.0.0",
310 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
311 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
312 | },
313 | "html-encoding-sniffer": {
314 | "version": "1.0.2",
315 | "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz",
316 | "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==",
317 | "requires": {
318 | "whatwg-encoding": "^1.0.1"
319 | }
320 | },
321 | "http-signature": {
322 | "version": "1.2.0",
323 | "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
324 | "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
325 | "requires": {
326 | "assert-plus": "^1.0.0",
327 | "jsprim": "^1.2.2",
328 | "sshpk": "^1.7.0"
329 | }
330 | },
331 | "iconv-lite": {
332 | "version": "0.4.24",
333 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
334 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
335 | "requires": {
336 | "safer-buffer": ">= 2.1.2 < 3"
337 | }
338 | },
339 | "ip-regex": {
340 | "version": "2.1.0",
341 | "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz",
342 | "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk="
343 | },
344 | "is-typedarray": {
345 | "version": "1.0.0",
346 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
347 | "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
348 | },
349 | "isstream": {
350 | "version": "0.1.2",
351 | "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
352 | "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
353 | },
354 | "jsbn": {
355 | "version": "0.1.1",
356 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
357 | "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
358 | },
359 | "jsdom": {
360 | "version": "15.2.1",
361 | "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-15.2.1.tgz",
362 | "integrity": "sha512-fAl1W0/7T2G5vURSyxBzrJ1LSdQn6Tr5UX/xD4PXDx/PDgwygedfW6El/KIj3xJ7FU61TTYnc/l/B7P49Eqt6g==",
363 | "requires": {
364 | "abab": "^2.0.0",
365 | "acorn": "^7.1.0",
366 | "acorn-globals": "^4.3.2",
367 | "array-equal": "^1.0.0",
368 | "cssom": "^0.4.1",
369 | "cssstyle": "^2.0.0",
370 | "data-urls": "^1.1.0",
371 | "domexception": "^1.0.1",
372 | "escodegen": "^1.11.1",
373 | "html-encoding-sniffer": "^1.0.2",
374 | "nwsapi": "^2.2.0",
375 | "parse5": "5.1.0",
376 | "pn": "^1.1.0",
377 | "request": "^2.88.0",
378 | "request-promise-native": "^1.0.7",
379 | "saxes": "^3.1.9",
380 | "symbol-tree": "^3.2.2",
381 | "tough-cookie": "^3.0.1",
382 | "w3c-hr-time": "^1.0.1",
383 | "w3c-xmlserializer": "^1.1.2",
384 | "webidl-conversions": "^4.0.2",
385 | "whatwg-encoding": "^1.0.5",
386 | "whatwg-mimetype": "^2.3.0",
387 | "whatwg-url": "^7.0.0",
388 | "ws": "^7.0.0",
389 | "xml-name-validator": "^3.0.0"
390 | }
391 | },
392 | "json-schema": {
393 | "version": "0.2.3",
394 | "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
395 | "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
396 | },
397 | "json-schema-traverse": {
398 | "version": "0.4.1",
399 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
400 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
401 | },
402 | "json-stringify-safe": {
403 | "version": "5.0.1",
404 | "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
405 | "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
406 | },
407 | "jsprim": {
408 | "version": "1.4.1",
409 | "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
410 | "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
411 | "requires": {
412 | "assert-plus": "1.0.0",
413 | "extsprintf": "1.3.0",
414 | "json-schema": "0.2.3",
415 | "verror": "1.10.0"
416 | }
417 | },
418 | "levn": {
419 | "version": "0.3.0",
420 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
421 | "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
422 | "requires": {
423 | "prelude-ls": "~1.1.2",
424 | "type-check": "~0.3.2"
425 | }
426 | },
427 | "lodash": {
428 | "version": "4.17.15",
429 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
430 | "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
431 | },
432 | "lodash.sortby": {
433 | "version": "4.7.0",
434 | "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
435 | "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg="
436 | },
437 | "mime-db": {
438 | "version": "1.42.0",
439 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz",
440 | "integrity": "sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ=="
441 | },
442 | "mime-types": {
443 | "version": "2.1.25",
444 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.25.tgz",
445 | "integrity": "sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg==",
446 | "requires": {
447 | "mime-db": "1.42.0"
448 | }
449 | },
450 | "nwsapi": {
451 | "version": "2.2.0",
452 | "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz",
453 | "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ=="
454 | },
455 | "oauth-sign": {
456 | "version": "0.9.0",
457 | "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
458 | "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="
459 | },
460 | "optionator": {
461 | "version": "0.8.3",
462 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
463 | "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
464 | "requires": {
465 | "deep-is": "~0.1.3",
466 | "fast-levenshtein": "~2.0.6",
467 | "levn": "~0.3.0",
468 | "prelude-ls": "~1.1.2",
469 | "type-check": "~0.3.2",
470 | "word-wrap": "~1.2.3"
471 | }
472 | },
473 | "parse5": {
474 | "version": "5.1.0",
475 | "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz",
476 | "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ=="
477 | },
478 | "performance-now": {
479 | "version": "2.1.0",
480 | "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
481 | "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
482 | },
483 | "pn": {
484 | "version": "1.1.0",
485 | "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz",
486 | "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA=="
487 | },
488 | "prelude-ls": {
489 | "version": "1.1.2",
490 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
491 | "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ="
492 | },
493 | "psl": {
494 | "version": "1.4.0",
495 | "resolved": "https://registry.npmjs.org/psl/-/psl-1.4.0.tgz",
496 | "integrity": "sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw=="
497 | },
498 | "punycode": {
499 | "version": "2.1.1",
500 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
501 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
502 | },
503 | "qs": {
504 | "version": "6.5.2",
505 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
506 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
507 | },
508 | "request": {
509 | "version": "2.88.0",
510 | "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
511 | "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
512 | "requires": {
513 | "aws-sign2": "~0.7.0",
514 | "aws4": "^1.8.0",
515 | "caseless": "~0.12.0",
516 | "combined-stream": "~1.0.6",
517 | "extend": "~3.0.2",
518 | "forever-agent": "~0.6.1",
519 | "form-data": "~2.3.2",
520 | "har-validator": "~5.1.0",
521 | "http-signature": "~1.2.0",
522 | "is-typedarray": "~1.0.0",
523 | "isstream": "~0.1.2",
524 | "json-stringify-safe": "~5.0.1",
525 | "mime-types": "~2.1.19",
526 | "oauth-sign": "~0.9.0",
527 | "performance-now": "^2.1.0",
528 | "qs": "~6.5.2",
529 | "safe-buffer": "^5.1.2",
530 | "tough-cookie": "~2.4.3",
531 | "tunnel-agent": "^0.6.0",
532 | "uuid": "^3.3.2"
533 | },
534 | "dependencies": {
535 | "punycode": {
536 | "version": "1.4.1",
537 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
538 | "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
539 | },
540 | "tough-cookie": {
541 | "version": "2.4.3",
542 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
543 | "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
544 | "requires": {
545 | "psl": "^1.1.24",
546 | "punycode": "^1.4.1"
547 | }
548 | }
549 | }
550 | },
551 | "request-promise-core": {
552 | "version": "1.1.3",
553 | "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.3.tgz",
554 | "integrity": "sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==",
555 | "requires": {
556 | "lodash": "^4.17.15"
557 | }
558 | },
559 | "request-promise-native": {
560 | "version": "1.0.8",
561 | "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.8.tgz",
562 | "integrity": "sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ==",
563 | "requires": {
564 | "request-promise-core": "1.1.3",
565 | "stealthy-require": "^1.1.1",
566 | "tough-cookie": "^2.3.3"
567 | },
568 | "dependencies": {
569 | "tough-cookie": {
570 | "version": "2.5.0",
571 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
572 | "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
573 | "requires": {
574 | "psl": "^1.1.28",
575 | "punycode": "^2.1.1"
576 | }
577 | }
578 | }
579 | },
580 | "safe-buffer": {
581 | "version": "5.2.0",
582 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
583 | "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg=="
584 | },
585 | "safer-buffer": {
586 | "version": "2.1.2",
587 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
588 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
589 | },
590 | "saxes": {
591 | "version": "3.1.11",
592 | "resolved": "https://registry.npmjs.org/saxes/-/saxes-3.1.11.tgz",
593 | "integrity": "sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g==",
594 | "requires": {
595 | "xmlchars": "^2.1.1"
596 | }
597 | },
598 | "source-map": {
599 | "version": "0.6.1",
600 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
601 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
602 | "optional": true
603 | },
604 | "sshpk": {
605 | "version": "1.16.1",
606 | "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
607 | "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
608 | "requires": {
609 | "asn1": "~0.2.3",
610 | "assert-plus": "^1.0.0",
611 | "bcrypt-pbkdf": "^1.0.0",
612 | "dashdash": "^1.12.0",
613 | "ecc-jsbn": "~0.1.1",
614 | "getpass": "^0.1.1",
615 | "jsbn": "~0.1.0",
616 | "safer-buffer": "^2.0.2",
617 | "tweetnacl": "~0.14.0"
618 | }
619 | },
620 | "stealthy-require": {
621 | "version": "1.1.1",
622 | "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
623 | "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks="
624 | },
625 | "supports-color": {
626 | "version": "7.1.0",
627 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
628 | "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
629 | "requires": {
630 | "has-flag": "^4.0.0"
631 | }
632 | },
633 | "symbol-tree": {
634 | "version": "3.2.4",
635 | "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
636 | "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="
637 | },
638 | "tough-cookie": {
639 | "version": "3.0.1",
640 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz",
641 | "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==",
642 | "requires": {
643 | "ip-regex": "^2.1.0",
644 | "psl": "^1.1.28",
645 | "punycode": "^2.1.1"
646 | }
647 | },
648 | "tr46": {
649 | "version": "1.0.1",
650 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
651 | "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=",
652 | "requires": {
653 | "punycode": "^2.1.0"
654 | }
655 | },
656 | "tunnel-agent": {
657 | "version": "0.6.0",
658 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
659 | "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
660 | "requires": {
661 | "safe-buffer": "^5.0.1"
662 | }
663 | },
664 | "tweetnacl": {
665 | "version": "0.14.5",
666 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
667 | "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
668 | },
669 | "type-check": {
670 | "version": "0.3.2",
671 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
672 | "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
673 | "requires": {
674 | "prelude-ls": "~1.1.2"
675 | }
676 | },
677 | "uri-js": {
678 | "version": "4.2.2",
679 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
680 | "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
681 | "requires": {
682 | "punycode": "^2.1.0"
683 | }
684 | },
685 | "uuid": {
686 | "version": "3.3.3",
687 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz",
688 | "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ=="
689 | },
690 | "verror": {
691 | "version": "1.10.0",
692 | "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
693 | "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
694 | "requires": {
695 | "assert-plus": "^1.0.0",
696 | "core-util-is": "1.0.2",
697 | "extsprintf": "^1.2.0"
698 | }
699 | },
700 | "w3c-hr-time": {
701 | "version": "1.0.1",
702 | "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz",
703 | "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=",
704 | "requires": {
705 | "browser-process-hrtime": "^0.1.2"
706 | }
707 | },
708 | "w3c-xmlserializer": {
709 | "version": "1.1.2",
710 | "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz",
711 | "integrity": "sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg==",
712 | "requires": {
713 | "domexception": "^1.0.1",
714 | "webidl-conversions": "^4.0.2",
715 | "xml-name-validator": "^3.0.0"
716 | }
717 | },
718 | "webidl-conversions": {
719 | "version": "4.0.2",
720 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
721 | "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg=="
722 | },
723 | "whatwg-encoding": {
724 | "version": "1.0.5",
725 | "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz",
726 | "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==",
727 | "requires": {
728 | "iconv-lite": "0.4.24"
729 | }
730 | },
731 | "whatwg-mimetype": {
732 | "version": "2.3.0",
733 | "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz",
734 | "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g=="
735 | },
736 | "whatwg-url": {
737 | "version": "7.1.0",
738 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz",
739 | "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==",
740 | "requires": {
741 | "lodash.sortby": "^4.7.0",
742 | "tr46": "^1.0.1",
743 | "webidl-conversions": "^4.0.2"
744 | }
745 | },
746 | "word-wrap": {
747 | "version": "1.2.3",
748 | "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
749 | "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ=="
750 | },
751 | "ws": {
752 | "version": "7.2.0",
753 | "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.0.tgz",
754 | "integrity": "sha512-+SqNqFbwTm/0DC18KYzIsMTnEWpLwJsiasW/O17la4iDRRIO9uaHbvKiAS3AHgTiuuWerK/brj4O6MYZkei9xg==",
755 | "requires": {
756 | "async-limiter": "^1.0.0"
757 | }
758 | },
759 | "xml-name-validator": {
760 | "version": "3.0.0",
761 | "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz",
762 | "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw=="
763 | },
764 | "xmlchars": {
765 | "version": "2.2.0",
766 | "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
767 | "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
768 | }
769 | }
770 | }
771 |
--------------------------------------------------------------------------------
/tme/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tme",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "bin": {
13 | "tme": "index.js"
14 | },
15 | "dependencies": {
16 | "chalk": "^3.0.0",
17 | "jsdom": "^15.2.1"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/tme/render.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const jsdom = require('jsdom');
3 | const { JSDOM } = jsdom;
4 |
5 | const render = async filename => {
6 | const filePath = path.join(process.cwd(), filename);
7 |
8 | const dom = await JSDOM.fromFile(filePath, {
9 | runScripts: 'dangerously',
10 | resources: 'usable'
11 | });
12 |
13 | return new Promise((resolve, reject) => {
14 | dom.window.document.addEventListener('DOMContentLoaded', () => {
15 | resolve(dom);
16 | });
17 | });
18 | };
19 |
20 | module.exports = render;
21 |
--------------------------------------------------------------------------------
/tme/runner.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 | const chalk = require('chalk');
4 | const render = require('./render');
5 |
6 | const forbiddenDirs = ['node_modules'];
7 |
8 | class Runner {
9 | constructor() {
10 | this.testFiles = [];
11 | }
12 |
13 | async runTests() {
14 | for (let file of this.testFiles) {
15 | console.log(chalk.gray(`---- ${file.shortName}`));
16 | const beforeEaches = [];
17 | global.render = render;
18 | global.beforeEach = fn => {
19 | beforeEaches.push(fn);
20 | };
21 | global.it = async (desc, fn) => {
22 | beforeEaches.forEach(func => func());
23 | try {
24 | await fn();
25 | console.log(chalk.green(`\tOK - ${desc}`));
26 | } catch (err) {
27 | const message = err.message.replace(/\n/g, '\n\t\t');
28 | console.log(chalk.red(`\tX - ${desc}`));
29 | console.log(chalk.red('\t', message));
30 | }
31 | };
32 |
33 | try {
34 | require(file.name);
35 | } catch (err) {
36 | console.log(chalk.red(err));
37 | }
38 | }
39 | }
40 |
41 | async collectFiles(targetPath) {
42 | const files = await fs.promises.readdir(targetPath);
43 |
44 | for (let file of files) {
45 | const filepath = path.join(targetPath, file);
46 | const stats = await fs.promises.lstat(filepath);
47 |
48 | if (stats.isFile() && file.includes('.test.js')) {
49 | this.testFiles.push({ name: filepath, shortName: file });
50 | } else if (stats.isDirectory() && !forbiddenDirs.includes(file)) {
51 | const childFiles = await fs.promises.readdir(filepath);
52 |
53 | files.push(...childFiles.map(f => path.join(file, f)));
54 | }
55 | }
56 | }
57 | }
58 |
59 | module.exports = Runner;
60 |
--------------------------------------------------------------------------------
/tme/sampleproject/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | forEach(arr, fn) {
3 | for (let element of arr) {
4 | fn(element);
5 | }
6 | }
7 | };
8 |
--------------------------------------------------------------------------------
/tme/sampleproject/test/forEach.test.js:
--------------------------------------------------------------------------------
1 | const assert = require('assert');
2 | const { forEach } = require('../index');
3 |
4 | let numbers;
5 | beforeEach(() => {
6 | numbers = [1, 2, 3];
7 | });
8 |
9 | it('should sum an array', () => {
10 | let total = 0;
11 | forEach(numbers, value => {
12 | total += value;
13 | });
14 |
15 | assert.strictEqual(total, 6);
16 | numbers.push(3);
17 | numbers.push(3);
18 | numbers.push(3);
19 | numbers.push(3);
20 | });
21 |
22 | it('beforeEach is ran each time', () => {
23 | assert.strictEqual(numbers.length, 4);
24 | });
25 |
--------------------------------------------------------------------------------
/tme/samplewebproject/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/tme/samplewebproject/index.js:
--------------------------------------------------------------------------------
1 | document.querySelector('form').addEventListener('submit', event => {
2 | event.preventDefault();
3 |
4 | const { value } = document.querySelector('input');
5 |
6 | const header = document.querySelector('h1');
7 | if (value.includes('@')) {
8 | header.innerHTML = 'Looks good!';
9 | } else {
10 | header.innerHTML = 'Invalid email';
11 | }
12 | });
13 |
--------------------------------------------------------------------------------
/tme/samplewebproject/test/app.test.js:
--------------------------------------------------------------------------------
1 | const assert = require('assert');
2 |
3 | it('has a text input', async () => {
4 | const dom = await render('index.html');
5 |
6 | const input = dom.window.document.querySelector('input');
7 |
8 | assert(input);
9 | });
10 |
11 | it('shows a success message with a valid email', async () => {
12 | const dom = await render('index.html');
13 |
14 | const input = dom.window.document.querySelector('input');
15 | input.value = 'alskdjf@aslkdjf.com';
16 | dom.window.document
17 | .querySelector('form')
18 | .dispatchEvent(new dom.window.Event('submit'));
19 |
20 | const h1 = dom.window.document.querySelector('h1');
21 |
22 | assert.strictEqual(h1.innerHTML, 'Looks good!');
23 | });
24 |
25 | it('shows a fail message with a invalid email', async () => {
26 | const dom = await render('index.html');
27 |
28 | const input = dom.window.document.querySelector('input');
29 | input.value = 'aasdfsdf';
30 | dom.window.document
31 | .querySelector('form')
32 | .dispatchEvent(new dom.window.Event('submit'));
33 |
34 | const h1 = dom.window.document.querySelector('h1');
35 |
36 | assert.strictEqual(h1.innerHTML, 'Invalid email');
37 | });
38 |
--------------------------------------------------------------------------------
/watchit/index.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | const debounce = require('lodash.debounce');
4 | const chokidar = require('chokidar');
5 | const program = require('caporal');
6 | const fs = require('fs');
7 | const { spawn } = require('child_process');
8 | const chalk = require('chalk');
9 |
10 | program
11 | .version('0.0.1')
12 | .argument('[filename]', 'Name of a file to execute')
13 | .action(async ({ filename }) => {
14 | const name = filename || 'index.js';
15 |
16 | try {
17 | await fs.promises.access(name);
18 | } catch (err) {
19 | throw new Error(`Could not find the file ${name}`);
20 | }
21 |
22 | let proc;
23 | const start = debounce(() => {
24 | if (proc) {
25 | proc.kill();
26 | }
27 | console.log(chalk.blue('>>>> Starting process...'));
28 | proc = spawn('node', [name], { stdio: 'inherit' });
29 | }, 100);
30 |
31 | chokidar
32 | .watch('.')
33 | .on('add', start)
34 | .on('change', start)
35 | .on('unlink', start);
36 | });
37 |
38 | program.parse(process.argv);
39 |
--------------------------------------------------------------------------------
/watchit/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "watchit",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "ansi": {
8 | "version": "0.3.1",
9 | "resolved": "https://registry.npmjs.org/ansi/-/ansi-0.3.1.tgz",
10 | "integrity": "sha1-DELU+xcWDVqa8eSEus4cZpIsGyE="
11 | },
12 | "ansi-escapes": {
13 | "version": "1.4.0",
14 | "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz",
15 | "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4="
16 | },
17 | "ansi-regex": {
18 | "version": "3.0.0",
19 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
20 | "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
21 | },
22 | "ansi-styles": {
23 | "version": "4.1.0",
24 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.1.0.tgz",
25 | "integrity": "sha512-Qts4KCLKG+waHc9C4m07weIY8qyeixoS0h6RnbsNVD6Fw+pEZGW3vTyObL3WXpE09Mq4Oi7/lBEyLmOiLtlYWQ==",
26 | "requires": {
27 | "color-convert": "^2.0.1"
28 | }
29 | },
30 | "anymatch": {
31 | "version": "3.1.1",
32 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz",
33 | "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==",
34 | "requires": {
35 | "normalize-path": "^3.0.0",
36 | "picomatch": "^2.0.4"
37 | }
38 | },
39 | "are-we-there-yet": {
40 | "version": "1.1.5",
41 | "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
42 | "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
43 | "requires": {
44 | "delegates": "^1.0.0",
45 | "readable-stream": "^2.0.6"
46 | }
47 | },
48 | "async": {
49 | "version": "1.0.0",
50 | "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz",
51 | "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k="
52 | },
53 | "binary-extensions": {
54 | "version": "2.0.0",
55 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz",
56 | "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow=="
57 | },
58 | "bluebird": {
59 | "version": "3.7.1",
60 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.1.tgz",
61 | "integrity": "sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg=="
62 | },
63 | "braces": {
64 | "version": "3.0.2",
65 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
66 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
67 | "requires": {
68 | "fill-range": "^7.0.1"
69 | }
70 | },
71 | "buffer-from": {
72 | "version": "1.1.1",
73 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
74 | "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
75 | },
76 | "caporal": {
77 | "version": "1.3.0",
78 | "resolved": "https://registry.npmjs.org/caporal/-/caporal-1.3.0.tgz",
79 | "integrity": "sha512-4bj21UXbEu5cF+1gVjhwwRqMhY3lR7CUTlBr6YX3uzftL4l/sbu8EoHfOLZWHr+HeiosW9fTWkQCS2UZMAk5lw==",
80 | "requires": {
81 | "bluebird": "^3.4.7",
82 | "cli-table3": "^0.5.0",
83 | "colorette": "^1.0.1",
84 | "fast-levenshtein": "^2.0.6",
85 | "lodash": "^4.17.14",
86 | "micromist": "1.1.0",
87 | "prettyjson": "^1.2.1",
88 | "tabtab": "^2.2.2",
89 | "winston": "^2.3.1"
90 | }
91 | },
92 | "chalk": {
93 | "version": "3.0.0",
94 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
95 | "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
96 | "requires": {
97 | "ansi-styles": "^4.1.0",
98 | "supports-color": "^7.1.0"
99 | }
100 | },
101 | "chokidar": {
102 | "version": "3.3.0",
103 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz",
104 | "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==",
105 | "requires": {
106 | "anymatch": "~3.1.1",
107 | "braces": "~3.0.2",
108 | "fsevents": "~2.1.1",
109 | "glob-parent": "~5.1.0",
110 | "is-binary-path": "~2.1.0",
111 | "is-glob": "~4.0.1",
112 | "normalize-path": "~3.0.0",
113 | "readdirp": "~3.2.0"
114 | }
115 | },
116 | "cli-cursor": {
117 | "version": "1.0.2",
118 | "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz",
119 | "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=",
120 | "requires": {
121 | "restore-cursor": "^1.0.1"
122 | }
123 | },
124 | "cli-table3": {
125 | "version": "0.5.1",
126 | "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz",
127 | "integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==",
128 | "requires": {
129 | "colors": "^1.1.2",
130 | "object-assign": "^4.1.0",
131 | "string-width": "^2.1.1"
132 | }
133 | },
134 | "cli-width": {
135 | "version": "2.2.0",
136 | "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz",
137 | "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk="
138 | },
139 | "code-point-at": {
140 | "version": "1.1.0",
141 | "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
142 | "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
143 | },
144 | "color-convert": {
145 | "version": "2.0.1",
146 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
147 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
148 | "requires": {
149 | "color-name": "~1.1.4"
150 | }
151 | },
152 | "color-name": {
153 | "version": "1.1.4",
154 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
155 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
156 | },
157 | "colorette": {
158 | "version": "1.1.0",
159 | "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.1.0.tgz",
160 | "integrity": "sha512-6S062WDQUXi6hOfkO/sBPVwE5ASXY4G2+b4atvhJfSsuUUhIaUKlkjLe9692Ipyt5/a+IPF5aVTu3V5gvXq5cg=="
161 | },
162 | "colors": {
163 | "version": "1.4.0",
164 | "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
165 | "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA=="
166 | },
167 | "concat-stream": {
168 | "version": "1.6.2",
169 | "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
170 | "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
171 | "requires": {
172 | "buffer-from": "^1.0.0",
173 | "inherits": "^2.0.3",
174 | "readable-stream": "^2.2.2",
175 | "typedarray": "^0.0.6"
176 | }
177 | },
178 | "core-util-is": {
179 | "version": "1.0.2",
180 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
181 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
182 | },
183 | "cycle": {
184 | "version": "1.0.3",
185 | "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz",
186 | "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI="
187 | },
188 | "debug": {
189 | "version": "2.6.9",
190 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
191 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
192 | "requires": {
193 | "ms": "2.0.0"
194 | }
195 | },
196 | "delegates": {
197 | "version": "1.0.0",
198 | "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
199 | "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
200 | },
201 | "escape-string-regexp": {
202 | "version": "1.0.5",
203 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
204 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
205 | },
206 | "exit-hook": {
207 | "version": "1.1.1",
208 | "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz",
209 | "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g="
210 | },
211 | "extend": {
212 | "version": "3.0.2",
213 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
214 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
215 | },
216 | "external-editor": {
217 | "version": "1.1.1",
218 | "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-1.1.1.tgz",
219 | "integrity": "sha1-Etew24UPf/fnCBuvQAVwAGDEYAs=",
220 | "requires": {
221 | "extend": "^3.0.0",
222 | "spawn-sync": "^1.0.15",
223 | "tmp": "^0.0.29"
224 | }
225 | },
226 | "eyes": {
227 | "version": "0.1.8",
228 | "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz",
229 | "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A="
230 | },
231 | "fast-levenshtein": {
232 | "version": "2.0.6",
233 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
234 | "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc="
235 | },
236 | "figures": {
237 | "version": "1.7.0",
238 | "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz",
239 | "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=",
240 | "requires": {
241 | "escape-string-regexp": "^1.0.5",
242 | "object-assign": "^4.1.0"
243 | }
244 | },
245 | "fill-range": {
246 | "version": "7.0.1",
247 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
248 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
249 | "requires": {
250 | "to-regex-range": "^5.0.1"
251 | }
252 | },
253 | "fsevents": {
254 | "version": "2.1.1",
255 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.1.tgz",
256 | "integrity": "sha512-4FRPXWETxtigtJW/gxzEDsX1LVbPAM93VleB83kZB+ellqbHMkyt2aJfuzNLRvFPnGi6bcE5SvfxgbXPeKteJw==",
257 | "optional": true
258 | },
259 | "gauge": {
260 | "version": "1.2.7",
261 | "resolved": "https://registry.npmjs.org/gauge/-/gauge-1.2.7.tgz",
262 | "integrity": "sha1-6c7FSD09TuDvRLYKfZnkk14TbZM=",
263 | "requires": {
264 | "ansi": "^0.3.0",
265 | "has-unicode": "^2.0.0",
266 | "lodash.pad": "^4.1.0",
267 | "lodash.padend": "^4.1.0",
268 | "lodash.padstart": "^4.1.0"
269 | }
270 | },
271 | "glob-parent": {
272 | "version": "5.1.0",
273 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz",
274 | "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==",
275 | "requires": {
276 | "is-glob": "^4.0.1"
277 | }
278 | },
279 | "has-ansi": {
280 | "version": "2.0.0",
281 | "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
282 | "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
283 | "requires": {
284 | "ansi-regex": "^2.0.0"
285 | },
286 | "dependencies": {
287 | "ansi-regex": {
288 | "version": "2.1.1",
289 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
290 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
291 | }
292 | }
293 | },
294 | "has-flag": {
295 | "version": "4.0.0",
296 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
297 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
298 | },
299 | "has-unicode": {
300 | "version": "2.0.1",
301 | "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
302 | "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
303 | },
304 | "inherits": {
305 | "version": "2.0.4",
306 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
307 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
308 | },
309 | "inquirer": {
310 | "version": "1.2.3",
311 | "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-1.2.3.tgz",
312 | "integrity": "sha1-TexvMvN+97sLLtPx0aXD9UUHSRg=",
313 | "requires": {
314 | "ansi-escapes": "^1.1.0",
315 | "chalk": "^1.0.0",
316 | "cli-cursor": "^1.0.1",
317 | "cli-width": "^2.0.0",
318 | "external-editor": "^1.1.0",
319 | "figures": "^1.3.5",
320 | "lodash": "^4.3.0",
321 | "mute-stream": "0.0.6",
322 | "pinkie-promise": "^2.0.0",
323 | "run-async": "^2.2.0",
324 | "rx": "^4.1.0",
325 | "string-width": "^1.0.1",
326 | "strip-ansi": "^3.0.0",
327 | "through": "^2.3.6"
328 | },
329 | "dependencies": {
330 | "ansi-regex": {
331 | "version": "2.1.1",
332 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
333 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
334 | },
335 | "ansi-styles": {
336 | "version": "2.2.1",
337 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
338 | "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
339 | },
340 | "chalk": {
341 | "version": "1.1.3",
342 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
343 | "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
344 | "requires": {
345 | "ansi-styles": "^2.2.1",
346 | "escape-string-regexp": "^1.0.2",
347 | "has-ansi": "^2.0.0",
348 | "strip-ansi": "^3.0.0",
349 | "supports-color": "^2.0.0"
350 | }
351 | },
352 | "is-fullwidth-code-point": {
353 | "version": "1.0.0",
354 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
355 | "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
356 | "requires": {
357 | "number-is-nan": "^1.0.0"
358 | }
359 | },
360 | "string-width": {
361 | "version": "1.0.2",
362 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
363 | "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
364 | "requires": {
365 | "code-point-at": "^1.0.0",
366 | "is-fullwidth-code-point": "^1.0.0",
367 | "strip-ansi": "^3.0.0"
368 | }
369 | },
370 | "strip-ansi": {
371 | "version": "3.0.1",
372 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
373 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
374 | "requires": {
375 | "ansi-regex": "^2.0.0"
376 | }
377 | },
378 | "supports-color": {
379 | "version": "2.0.0",
380 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
381 | "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
382 | }
383 | }
384 | },
385 | "is-binary-path": {
386 | "version": "2.1.0",
387 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
388 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
389 | "requires": {
390 | "binary-extensions": "^2.0.0"
391 | }
392 | },
393 | "is-extglob": {
394 | "version": "2.1.1",
395 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
396 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI="
397 | },
398 | "is-fullwidth-code-point": {
399 | "version": "2.0.0",
400 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
401 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
402 | },
403 | "is-glob": {
404 | "version": "4.0.1",
405 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
406 | "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
407 | "requires": {
408 | "is-extglob": "^2.1.1"
409 | }
410 | },
411 | "is-number": {
412 | "version": "7.0.0",
413 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
414 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
415 | },
416 | "is-promise": {
417 | "version": "2.1.0",
418 | "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
419 | "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o="
420 | },
421 | "isarray": {
422 | "version": "1.0.0",
423 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
424 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
425 | },
426 | "isstream": {
427 | "version": "0.1.2",
428 | "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
429 | "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
430 | },
431 | "lodash": {
432 | "version": "4.17.15",
433 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
434 | "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
435 | },
436 | "lodash.camelcase": {
437 | "version": "4.3.0",
438 | "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
439 | "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY="
440 | },
441 | "lodash.debounce": {
442 | "version": "4.0.8",
443 | "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
444 | "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168="
445 | },
446 | "lodash.difference": {
447 | "version": "4.5.0",
448 | "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz",
449 | "integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw="
450 | },
451 | "lodash.pad": {
452 | "version": "4.5.1",
453 | "resolved": "https://registry.npmjs.org/lodash.pad/-/lodash.pad-4.5.1.tgz",
454 | "integrity": "sha1-QzCUmoM6fI2iLMIPaibE1Z3runA="
455 | },
456 | "lodash.padend": {
457 | "version": "4.6.1",
458 | "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz",
459 | "integrity": "sha1-U8y6BH0G4VjTEfRdpiX05J5vFm4="
460 | },
461 | "lodash.padstart": {
462 | "version": "4.6.1",
463 | "resolved": "https://registry.npmjs.org/lodash.padstart/-/lodash.padstart-4.6.1.tgz",
464 | "integrity": "sha1-0uPuv/DZ05rVD1y9G1KnvOa7YRs="
465 | },
466 | "lodash.uniq": {
467 | "version": "4.5.0",
468 | "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
469 | "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M="
470 | },
471 | "micromist": {
472 | "version": "1.1.0",
473 | "resolved": "https://registry.npmjs.org/micromist/-/micromist-1.1.0.tgz",
474 | "integrity": "sha512-+CQ76pabE9egniSEdmDuH+j2cYyIBKP97kujG8ZLZyLCRq5ExwtIy4DPHPFrq4jVbhMRBnyjuH50KU9Ohs8QCg==",
475 | "requires": {
476 | "lodash.camelcase": "^4.3.0"
477 | }
478 | },
479 | "minimist": {
480 | "version": "1.2.0",
481 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
482 | "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
483 | },
484 | "mkdirp": {
485 | "version": "0.5.1",
486 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
487 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
488 | "requires": {
489 | "minimist": "0.0.8"
490 | },
491 | "dependencies": {
492 | "minimist": {
493 | "version": "0.0.8",
494 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
495 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
496 | }
497 | }
498 | },
499 | "ms": {
500 | "version": "2.0.0",
501 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
502 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
503 | },
504 | "mute-stream": {
505 | "version": "0.0.6",
506 | "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.6.tgz",
507 | "integrity": "sha1-SJYrGeFp/R38JAs/HnMXYnu8R9s="
508 | },
509 | "normalize-path": {
510 | "version": "3.0.0",
511 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
512 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
513 | },
514 | "npmlog": {
515 | "version": "2.0.4",
516 | "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-2.0.4.tgz",
517 | "integrity": "sha1-mLUlMPJRTKkNCexbIsiEZyI3VpI=",
518 | "requires": {
519 | "ansi": "~0.3.1",
520 | "are-we-there-yet": "~1.1.2",
521 | "gauge": "~1.2.5"
522 | }
523 | },
524 | "number-is-nan": {
525 | "version": "1.0.1",
526 | "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
527 | "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
528 | },
529 | "object-assign": {
530 | "version": "4.1.1",
531 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
532 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
533 | },
534 | "onetime": {
535 | "version": "1.1.0",
536 | "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz",
537 | "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k="
538 | },
539 | "os-shim": {
540 | "version": "0.1.3",
541 | "resolved": "https://registry.npmjs.org/os-shim/-/os-shim-0.1.3.tgz",
542 | "integrity": "sha1-a2LDeRz3kJ6jXtRuF2WLtBfLORc="
543 | },
544 | "os-tmpdir": {
545 | "version": "1.0.2",
546 | "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
547 | "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
548 | },
549 | "picomatch": {
550 | "version": "2.1.1",
551 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.1.1.tgz",
552 | "integrity": "sha512-OYMyqkKzK7blWO/+XZYP6w8hH0LDvkBvdvKukti+7kqYFCiEAk+gI3DWnryapc0Dau05ugGTy0foQ6mqn4AHYA=="
553 | },
554 | "pinkie": {
555 | "version": "2.0.4",
556 | "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
557 | "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA="
558 | },
559 | "pinkie-promise": {
560 | "version": "2.0.1",
561 | "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
562 | "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
563 | "requires": {
564 | "pinkie": "^2.0.0"
565 | }
566 | },
567 | "prettyjson": {
568 | "version": "1.2.1",
569 | "resolved": "https://registry.npmjs.org/prettyjson/-/prettyjson-1.2.1.tgz",
570 | "integrity": "sha1-/P+rQdGcq0365eV15kJGYZsS0ok=",
571 | "requires": {
572 | "colors": "^1.1.2",
573 | "minimist": "^1.2.0"
574 | }
575 | },
576 | "process-nextick-args": {
577 | "version": "2.0.1",
578 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
579 | "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
580 | },
581 | "readable-stream": {
582 | "version": "2.3.6",
583 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
584 | "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
585 | "requires": {
586 | "core-util-is": "~1.0.0",
587 | "inherits": "~2.0.3",
588 | "isarray": "~1.0.0",
589 | "process-nextick-args": "~2.0.0",
590 | "safe-buffer": "~5.1.1",
591 | "string_decoder": "~1.1.1",
592 | "util-deprecate": "~1.0.1"
593 | }
594 | },
595 | "readdirp": {
596 | "version": "3.2.0",
597 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz",
598 | "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==",
599 | "requires": {
600 | "picomatch": "^2.0.4"
601 | }
602 | },
603 | "restore-cursor": {
604 | "version": "1.0.1",
605 | "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz",
606 | "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=",
607 | "requires": {
608 | "exit-hook": "^1.0.0",
609 | "onetime": "^1.0.0"
610 | }
611 | },
612 | "run-async": {
613 | "version": "2.3.0",
614 | "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
615 | "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=",
616 | "requires": {
617 | "is-promise": "^2.1.0"
618 | }
619 | },
620 | "rx": {
621 | "version": "4.1.0",
622 | "resolved": "https://registry.npmjs.org/rx/-/rx-4.1.0.tgz",
623 | "integrity": "sha1-pfE/957zt0D+MKqAP7CfmIBdR4I="
624 | },
625 | "safe-buffer": {
626 | "version": "5.1.2",
627 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
628 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
629 | },
630 | "spawn-sync": {
631 | "version": "1.0.15",
632 | "resolved": "https://registry.npmjs.org/spawn-sync/-/spawn-sync-1.0.15.tgz",
633 | "integrity": "sha1-sAeZVX63+wyDdsKdROih6mfldHY=",
634 | "requires": {
635 | "concat-stream": "^1.4.7",
636 | "os-shim": "^0.1.2"
637 | }
638 | },
639 | "stack-trace": {
640 | "version": "0.0.10",
641 | "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
642 | "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA="
643 | },
644 | "string-width": {
645 | "version": "2.1.1",
646 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
647 | "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
648 | "requires": {
649 | "is-fullwidth-code-point": "^2.0.0",
650 | "strip-ansi": "^4.0.0"
651 | }
652 | },
653 | "string_decoder": {
654 | "version": "1.1.1",
655 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
656 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
657 | "requires": {
658 | "safe-buffer": "~5.1.0"
659 | }
660 | },
661 | "strip-ansi": {
662 | "version": "4.0.0",
663 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
664 | "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
665 | "requires": {
666 | "ansi-regex": "^3.0.0"
667 | }
668 | },
669 | "supports-color": {
670 | "version": "7.1.0",
671 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
672 | "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
673 | "requires": {
674 | "has-flag": "^4.0.0"
675 | }
676 | },
677 | "tabtab": {
678 | "version": "2.2.2",
679 | "resolved": "https://registry.npmjs.org/tabtab/-/tabtab-2.2.2.tgz",
680 | "integrity": "sha1-egR/FDsBC0y9MfhX6ClhUSy/ThQ=",
681 | "requires": {
682 | "debug": "^2.2.0",
683 | "inquirer": "^1.0.2",
684 | "lodash.difference": "^4.5.0",
685 | "lodash.uniq": "^4.5.0",
686 | "minimist": "^1.2.0",
687 | "mkdirp": "^0.5.1",
688 | "npmlog": "^2.0.3",
689 | "object-assign": "^4.1.0"
690 | }
691 | },
692 | "through": {
693 | "version": "2.3.8",
694 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
695 | "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
696 | },
697 | "tmp": {
698 | "version": "0.0.29",
699 | "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.29.tgz",
700 | "integrity": "sha1-8lEl/w3Z2jzLDC3Tce4SiLuRKMA=",
701 | "requires": {
702 | "os-tmpdir": "~1.0.1"
703 | }
704 | },
705 | "to-regex-range": {
706 | "version": "5.0.1",
707 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
708 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
709 | "requires": {
710 | "is-number": "^7.0.0"
711 | }
712 | },
713 | "typedarray": {
714 | "version": "0.0.6",
715 | "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
716 | "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
717 | },
718 | "util-deprecate": {
719 | "version": "1.0.2",
720 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
721 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
722 | },
723 | "winston": {
724 | "version": "2.4.4",
725 | "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.4.tgz",
726 | "integrity": "sha512-NBo2Pepn4hK4V01UfcWcDlmiVTs7VTB1h7bgnB0rgP146bYhMxX0ypCz3lBOfNxCO4Zuek7yeT+y/zM1OfMw4Q==",
727 | "requires": {
728 | "async": "~1.0.0",
729 | "colors": "1.0.x",
730 | "cycle": "1.0.x",
731 | "eyes": "0.1.x",
732 | "isstream": "0.1.x",
733 | "stack-trace": "0.0.x"
734 | },
735 | "dependencies": {
736 | "colors": {
737 | "version": "1.0.3",
738 | "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz",
739 | "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs="
740 | }
741 | }
742 | }
743 | }
744 | }
745 |
--------------------------------------------------------------------------------
/watchit/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "watchit",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "bin": {
13 | "watchit": "index.js"
14 | },
15 | "dependencies": {
16 | "caporal": "^1.3.0",
17 | "chalk": "^3.0.0",
18 | "chokidar": "^3.3.0",
19 | "lodash.debounce": "^4.0.8"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/watchit/test.js:
--------------------------------------------------------------------------------
1 | setInterval(() => {
2 | console.log('bye there!');
3 | }, 1000);
4 |
--------------------------------------------------------------------------------