UNPKG

10.1 kBJavaScriptView Raw
1const fs = require('fs');
2const path = require('path');
3const spawn = require('child_process').spawn;
4const entries = require('object.entries');
5const elegantSpinner = require('elegant-spinner');
6const logUpdate = require('log-update');
7const colors = require('colors');
8const pkg = require('../package.json');
9
10const spacer = ''.hidden;
11const listitem = '';
12const listitem1 = '▪︎'.yellow + '▪︎'.yellow.dim;
13const listitem2 = ''; /* '✨ ◼︎◻︎';*/
14const spinner = elegantSpinner();
15const workingDir = process.cwd();
16
17let start = 0;
18let started = 0;
19let finished = 0;
20let ivalMain = null;
21let ivalSpinner = null;
22let cmdArgs = {};
23let currentPath = ''; /* eslint no-unused-vars: 0 */
24let projectDirName = '';
25
26const next = () => {
27 finished = started;
28};
29
30const trimLeft = str => str.replace(/^\s+/, '');
31const getTimestamp = () => Date.now();
32const getTimer = startx => Date.now() - startx;
33
34const getPackage = () => require(path.join(workingDir, '/', projectDirName, '/package.json'));
35
36const startSpinner = (msg) => {
37 clearInterval(ivalSpinner);
38
39 let text = '';
40 msg = msg || 'installing packages';
41 if (!cmdArgs.quiet) {
42 text = colors.bold(msg + ' ');
43 } else {
44 text = colors.white('please wait during installation ');
45 }
46
47 ivalSpinner = setInterval(() => {
48 let counterText = '';
49 if (!cmdArgs.quiet) {
50 counterText = colors.dim(' (' + getTimer(start) + ' ms)');
51 }
52 logUpdate(spacer + text + colors.yellow(spinner()) + counterText);
53 }, 50);
54};
55
56const log = (msg) => {
57 if (!cmdArgs.quiet) {
58 console.log(spacer + msg);
59 }
60 next();
61};
62
63const logVersionWarning = (str) => {
64 const latestPackageVersion = str.trim();
65 if (latestPackageVersion && latestPackageVersion !== pkg.version) {
66 console.log(spacer + colors.magenta('v' + latestPackageVersion + ' is available!'));
67 }
68 next();
69};
70
71const chdir = (dir) => {
72 try {
73 process.chdir(dir);
74 next();
75 } catch (err) {
76 console.log('CHDIR:ERROR: ' + err);
77 }
78};
79
80const createFolder = (parts, i) => {
81 if (typeof parts[i] !== 'undefined') {
82 currentPath += '/' + parts[i];
83 next();
84 }
85 const dir = path.join.apply(null, parts.slice(0, i));
86 fs.existsSync(dir) || fs.mkdirSync(dir);
87};
88
89const createFolders = (dirPath) => {
90 const incl = str => dirPath.includes(str);
91
92 if (
93 (incl('vscode') && !cmdArgs.flow) ||
94 (incl('flow-typed') && !cmdArgs.flow) ||
95 (incl('mocks') && !cmdArgs.test) ||
96 (incl('mocks') && cmdArgs.styled)
97 ) {
98 next();
99 return;
100 }
101 dirPath = './' + projectDirName + '/' + dirPath;
102 const parts = dirPath.split(path.sep);
103 currentPath = '.';
104 for (let i = 1; i <= parts.length; i += 1) {
105 if (i !== parts.length) {
106 createFolder(parts, i);
107 } else {
108 createFolder(parts, i, next);
109 }
110 }
111};
112
113const writeFile = (filePath, content) => {
114 const incl = str => filePath.includes(str);
115
116 if (
117 (incl('eslint') && !cmdArgs.lint) ||
118 (incl('jest') && !cmdArgs.test) ||
119 (incl('spec') && !cmdArgs.test) ||
120 (incl('mocks') && !cmdArgs.test) ||
121 (incl('mocks') && cmdArgs.styled) ||
122 (incl('state') && !cmdArgs.redux && !cmdArgs.mobx) ||
123 (incl('store') && !cmdArgs.redux) ||
124 (incl('vscode') && !cmdArgs.flow) ||
125 (incl('app/types') && !cmdArgs.flow && !cmdArgs.mobx) ||
126 (incl('app/style.') && cmdArgs.styled) ||
127 (incl('app/styled.') && !cmdArgs.styled) ||
128 (incl('flowConfig') && !cmdArgs.flow) ||
129 (incl('flow-typed') && !cmdArgs.flow) ||
130 (incl('flow-typed') && incl('redux') && !cmdArgs.redux) ||
131 (incl('flow-typed/mobx') && !cmdArgs.mobx) ||
132 (incl('flow-typed/react-router-dom') && !cmdArgs.router) ||
133 (incl('flow-typed/styled-components') && !cmdArgs.styled)
134 ) {
135 next();
136 return;
137 }
138
139 if (content) {
140 content = trimLeft(content);
141 }
142
143 fs.writeFile(path.join(workingDir, '/', projectDirName, '/', filePath), content, (err) => {
144 if (err) return log(err);
145 let fileName = filePath.substring(filePath.lastIndexOf('/') + 1);
146 fileName = colors.yellow(fileName);
147 filePath = filePath.substring(0, filePath.lastIndexOf('/'));
148 filePath = filePath.length > 0 ? colors.white(filePath + '/') : '';
149 const msg = listitem + filePath + fileName;
150 log(msg);
151 return next();
152 });
153};
154
155const execCommand = (cmdString, options = {}) => {
156 if (options.dependencies && !cmdArgs.install) {
157 setTimeout(next, 0);
158 return;
159 }
160 !options.version && !options.callback && startSpinner();
161
162 const cmd = cmdString.split(' ').slice(0, 1)[0];
163 const args = cmdString.split(' ').slice(1);
164 const prc = spawn(cmd, args);
165 let result = '';
166
167 prc.stdout.setEncoding('utf8');
168
169 prc.stdout.on('data', (data) => {
170 const str = data.toString();
171 if (options.version) {
172 logVersionWarning(str);
173 } else if (options.callback) {
174 result = str.trim();
175 }
176 });
177
178 prc.stdout.on('end', () => {
179 if (options.callback) {
180 options.callback(result);
181 } else {
182 setTimeout(next, 0);
183 }
184 });
185};
186
187const sortObject = (obj) => {
188 const tempArray = [];
189 const tempObject = {};
190 Object.keys(obj).forEach(key => tempArray.push(key));
191 tempArray.sort().forEach((item) => {
192 tempObject[item] = obj[item];
193 });
194 return tempObject;
195};
196
197const getVersions = (dependencies, devDependencies) => {
198 if (cmdArgs.install) {
199 setTimeout(next, 0);
200 return;
201 }
202 dependencies = dependencies.trim().split(' ');
203 devDependencies = devDependencies.trim().split(' ');
204
205 let dependenciesCounter = dependencies.length + devDependencies.length;
206 const dependenciesObject = {};
207 const devDependenciesObject = {};
208
209 const get = (deps, obj) => {
210 const depsObject = {};
211 deps.forEach((dep) => {
212 execCommand('npm view ' + dep + ' dist-tags.latest', {
213 callback: (version) => {
214 obj[dep] = '^' + version;
215 dependenciesCounter -= 1;
216 }
217 });
218
219 if (dep === 'eslint-config-airbnb') {
220 dependenciesCounter += 1;
221 execCommand('npm info eslint-config-airbnb@latest peerDependencies', {
222 callback: (res) => {
223 res = res.replace(/'/g, '"');
224 res = res.replace(/eslint:/g, '"eslint":');
225 const airbnbVersions = JSON.parse(res);
226 Object.keys(airbnbVersions).forEach((key) => {
227 obj[key] = airbnbVersions[key];
228 });
229 dependenciesCounter -= 1;
230 }
231 });
232 }
233 });
234 };
235
236 get(dependencies, dependenciesObject);
237 get(devDependencies, devDependenciesObject);
238
239 const ival = setInterval(() => {
240 startSpinner('get latest version numbers');
241 if (dependenciesCounter === 0) {
242 clearInterval(ivalSpinner);
243 clearInterval(ival);
244 setTimeout(() => {
245 const pkgApp = getPackage();
246 pkgApp.dependencies = sortObject(dependenciesObject);
247 pkgApp.devDependencies = sortObject(devDependenciesObject);
248 writeFile('package.json', JSON.stringify(pkgApp, null, 2));
249 }, 0);
250 }
251 }, 50);
252};
253
254const sequence = (actions) => {
255 started -= 1;
256 ivalMain = setInterval(() => {
257 if (actions.length !== finished && (started === finished || started === -1)) {
258 clearInterval(ivalSpinner);
259 started += 1;
260 if (actions.length > started) {
261 const args = actions[started].slice(1);
262 if (args.length === 1) {
263 actions[started][0].call(this, args[0]);
264 } else {
265 actions[started][0].apply(this, args);
266 }
267 } else {
268 startSpinner();
269 }
270 }
271 }, 100);
272};
273
274const displayDependencies = () => {
275 const pkgApp = getPackage();
276 const dependencies = entries(Object.assign(pkgApp.dependencies, pkgApp.devDependencies));
277 dependencies.sort();
278 dependencies.forEach((dependency) => {
279 log(
280 listitem +
281 colors.white(dependency[0]) + ': '.white +
282 colors.yellow(dependency[1])
283 );
284 });
285};
286
287const displayAvailableScripts = () => {
288 const pkgApp = getPackage();
289 const port = pkgApp.scripts.start.split('--port ')[1];
290 const scripts = entries(Object.assign({}, pkgApp.scripts));
291 scripts.forEach((script) => {
292 log(colors.white('yarn ' + script[0]));
293 script[0] === 'start' && log(colors.white('open http://localhost:' + port + '/'));
294 });
295};
296
297const countAllNodeModules = () => {
298 let counter = 0;
299 if (cmdArgs.install) {
300 const dir = path.join(workingDir, '/', projectDirName, '/node_modules/');
301 const files = fs.readdirSync(dir);
302 files.forEach((file) => {
303 if (fs.statSync(dir + file).isDirectory()) {
304 if (file.charAt(0) !== '.') {
305 counter += 1;
306 }
307 }
308 });
309 }
310 return counter;
311};
312
313const exit = () => {
314 const pkgApp = getPackage();
315 const port = pkgApp.scripts.start.split('--port ')[1];
316 const secs = getTimer(start) / 1000;
317 const finishedMsg = listitem2 + colors.green('Done in ' + secs.toFixed(2) + 's.');
318 const numModules = countAllNodeModules();
319 let listMsg;
320
321 if (!cmdArgs.quiet) {
322 if (cmdArgs.install) {
323 listMsg = 'installed packages';
324 } else {
325 listMsg = 'latest versions';
326 }
327 logUpdate(spacer + listMsg.bold);
328 displayDependencies();
329 log('');
330
331 if (numModules > 0) {
332 log(colors.dim(spacer + '(' + numModules + ' node modules)'));
333 log('');
334 }
335
336 log(colors.bold(finishedMsg));
337 log('');
338 } else {
339 logUpdate(spacer + finishedMsg + ' \n');
340 }
341
342 log('usage'.bold);
343 log(colors.white('cd ' + projectDirName));
344 if (!cmdArgs.install) {
345 log(colors.white('yarn install'));
346 }
347 displayAvailableScripts();
348 log('');
349
350 clearInterval(ivalMain);
351 clearInterval(ivalSpinner);
352};
353
354const sleep = (delay) => {
355 setTimeout(() => {
356 next();
357 }, delay);
358};
359
360const starting = (headertext) => {
361 start = getTimestamp();
362 log('');
363 log(colors.bold(headertext));
364 next();
365};
366
367const init = (args, dir) => {
368 cmdArgs = args;
369 projectDirName = dir;
370 next();
371};
372
373module.exports = {
374 chdir,
375 createFolders,
376 exec: execCommand,
377 exit,
378 getTimestamp,
379 getTimer,
380 init,
381 log,
382 next,
383 sequence,
384 sleep,
385 starting,
386 versions: getVersions,
387 writeFile
388};