stack-analyze 1.2.0 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.mjs CHANGED
@@ -9,17 +9,26 @@ import {
9
9
  bios
10
10
  } from "systeminformation";
11
11
  import Wappalyzer from "wappalyzer";
12
- import cheerio from "cheerio";
12
+ import { load } from "cheerio";
13
+
14
+ const gigabyteConvert = (size, base=1073741824) => (size / base).toFixed(2);
15
+
16
+ const wappalyzer = new Wappalyzer();
17
+
18
+ const pagespeedApi = async (url, strategy) => await axios.get("https://www.googleapis.com/pagespeedonline/v5/runPagespeed", {
19
+ params: {
20
+ url,
21
+ key: "AIzaSyBEDaW4FxSZ2s1vz5CdD5Ai6PGZGdAzij0",
22
+ strategy
23
+ }
24
+ });
13
25
 
14
26
  const animeSearch = async ({ query, results, failed }) => {
15
27
  /* error manager */
16
28
  try {
17
29
  // call api
18
30
  const { data } = await axios.get("https://api.jikan.moe/v/anime", {
19
- params: {
20
- q: query,
21
- limit: 10
22
- }
31
+ params: { q: query }
23
32
  });
24
33
 
25
34
  results(data.results);
@@ -31,9 +40,7 @@ const bitlyInfo = async ({ link, token, results, failed }) => {
31
40
  try {
32
41
  const { data } = await axios.post(
33
42
  "https://api-ssl.bitly.com/v4/expand",
34
- {
35
- bitlink_id: link
36
- },
43
+ { bitlink_id: link },
37
44
  {
38
45
  headers: {
39
46
  Authorization: `Bearer ${token}`,
@@ -51,10 +58,7 @@ const cryptoMarket = async ({results, failed}) => {
51
58
  // start crypto
52
59
  const { data } = await axios.get(
53
60
  "https://api.coingecko.com/api/v3/coins/markets", {
54
- params: {
55
- vs_currency: "usd",
56
- per_page: 10
57
- }
61
+ params: { vs_currency: "usd" }
58
62
  }
59
63
  );
60
64
 
@@ -112,11 +116,11 @@ async function ramMemInfo(callback) {
112
116
 
113
117
  // show results
114
118
  callback({
115
- total_mem: `${(total / 1073741824).toFixed(2)} GB`,
116
- free_mem: `${(free / 1073741824).toFixed(2)} GB`,
117
- used_mem: `${(used / 1073741824).toFixed(2)} GB`,
118
- active_mem: `${(active / 1073741824).toFixed(2)} GB`,
119
- available_mem: `${(available / 1073741824).toFixed(2)} GB`
119
+ total_mem: `${gigabyteConvert(total)} GB`,
120
+ free_mem: `${gigabyteConvert(free)} GB`,
121
+ used_mem: `${gigabyteConvert(used)} GB`,
122
+ active_mem: `${gigabyteConvert(active)} GB`,
123
+ available_mem: `${gigabyteConvert(available)} GB`
120
124
  });
121
125
  } catch (err) { callback(err); }
122
126
  }
@@ -162,7 +166,7 @@ async function diskInfo(callback) {
162
166
  type,
163
167
  name,
164
168
  vendor,
165
- diskSize: `${(size / 1073741824).toFixed(2)} GB`,
169
+ diskSize: `${gigabyteConvert(size)} GB`,
166
170
  interfaceType
167
171
  }));
168
172
 
@@ -184,7 +188,7 @@ async function controllerInfo(callback) {
184
188
  vendor,
185
189
  vramSize: vram < 1024
186
190
  ? `${vram} MB`
187
- : `${(vram / 1024).toFixed(2)} GB`
191
+ : `${gigabyteConvert(vram, 1024)} GB`
188
192
  }));
189
193
 
190
194
  callback(controllersList);
@@ -229,11 +233,7 @@ async function biosInfo(callback) {
229
233
  const movieDB = async ({ api_key, query, results, failed }) => {
230
234
  try {
231
235
  const { data } = await axios.get("https://api.themoviedb.org/3/search/movie", {
232
- params: {
233
- api_key,
234
- query,
235
- page: 1
236
- }
236
+ params: { api_key, query, }
237
237
  });
238
238
 
239
239
  const movieData = data.results
@@ -257,7 +257,7 @@ const movieDB = async ({ api_key, query, results, failed }) => {
257
257
 
258
258
  return primaryDate.getTime() - secondaryDate.getTime();
259
259
  })
260
- .filter(({ release_date }) => release_date !== undefined && release_date !== "");
260
+ .filter((data) => data?.release_date);
261
261
 
262
262
  results(movieData);
263
263
  } catch (err) { failed(err); }
@@ -265,7 +265,7 @@ const movieDB = async ({ api_key, query, results, failed }) => {
265
265
 
266
266
  async function multipleStack({ urls, results, failed }) {
267
267
  let result;
268
- const wappalyzer = new Wappalyzer();
268
+
269
269
  try {
270
270
  await wappalyzer.init();
271
271
  result = await Promise.all(
@@ -284,21 +284,9 @@ async function multipleStack({ urls, results, failed }) {
284
284
 
285
285
  const pageSpeed = async ({ url, results, failed }) => {
286
286
  try {
287
- const resMobile = await axios.get("https://www.googleapis.com/pagespeedonline/v5/runPagespeed", {
288
- params: {
289
- url,
290
- key: "AIzaSyBEDaW4FxSZ2s1vz5CdD5Ai6PGZGdAzij0",
291
- strategy: "mobile"
292
- }
293
- });
287
+ const resMobile = await pagespeedApi(url, "mobile");
294
288
 
295
- const resDesktop = await axios.get("https://www.googleapis.com/pagespeedonline/v5/runPagespeed", {
296
- params: {
297
- url,
298
- key: "AIzaSyBEDaW4FxSZ2s1vz5CdD5Ai6PGZGdAzij0",
299
- strategy: "desktop"
300
- }
301
- });
289
+ const resDesktop = await pagespeedApi(url, "desktop");
302
290
 
303
291
  // extract results
304
292
  const mobile = Math.round(resMobile.data.lighthouseResult.categories.performance.score * 100);
@@ -309,9 +297,8 @@ const pageSpeed = async ({ url, results, failed }) => {
309
297
  };
310
298
 
311
299
  async function singleStack({ url, results, failed }) {
312
- const wappalyzer = await new Wappalyzer;
313
-
314
300
  let result;
301
+
315
302
  try {
316
303
  await wappalyzer.init();
317
304
 
@@ -334,8 +321,9 @@ async function singleStack({ url, results, failed }) {
334
321
 
335
322
  async function twitchInfo({ query, token, clientID, results, failed }) {
336
323
  try {
337
- const { data: twitchData } = await axios.get(`https://api.twitch.tv/helix/users?login=${query}`,
324
+ const { data: twitchData } = await axios.get("https://api.twitch.tv/helix/users",
338
325
  {
326
+ params: { login: query },
339
327
  headers: {
340
328
  Authorization: `Bearer ${token}`,
341
329
  "Client-Id": clientID
@@ -346,131 +334,95 @@ async function twitchInfo({ query, token, clientID, results, failed }) {
346
334
  } catch (err) { failed(err); }
347
335
  }
348
336
 
349
- function scrape(url) {
350
- let $;
351
-
352
- const scraping = axios.create({
353
- baseURL: url
354
- });
355
-
356
- const title = async ({results, failed}) => {
357
- try {
358
- const { data } = await scraping.get(url);
359
- $ = cheerio.load(data);
360
-
361
- results($("title").text());
362
- } catch (err) { failed(err); }
363
- };
364
-
365
- const images = async ({results, failed}) => {
366
- try {
367
- const { data } = await scraping.get(url);
368
- $ = cheerio.load(data);
369
-
370
- const imgs = $("img").map((i, el) => ({
371
- imagePath: $(el).attr("src"),
372
- imageTitle: $(el).attr("alt")
373
- })).toArray();
374
-
375
- results(imgs);
376
- } catch (err) { failed(err); }
377
- };
378
-
379
- const metadata = async ({results, failed}) => {
380
- try {
381
- const { data } = await scraping.get(url);
382
- $ = cheerio.load(data);
383
-
384
- const metadataList = $("meta").map((i, el) => ({
385
- metaInfo: $(el).attr("name"),
386
- metaContent: $(el).attr("content")
387
- })).toArray()
388
- .filter(({ metaInfo }) => metaInfo !== undefined);
389
-
390
- results(metadataList);
391
- } catch (err) { failed(err); }
392
- };
393
-
394
- const headings = async ({results, failed}) => {
395
- try {
396
- const { data } = await scraping.get(url);
397
- $ = cheerio.load(data);
398
-
399
- const headingList = $("h1, h2, h3, h4, h5, h6").map((i, el) => ({
400
- headingTag: $(el).prop("tagName"),
401
- headingText: $(el).text()
402
- })).toArray();
403
-
404
- results(headingList);
405
- } catch (err) { failed(err); }
406
- };
407
-
408
- const table_heading = async ({results, failed}) => {
409
- try {
410
- const { data } = await scraping.get(url);
411
- $ = cheerio.load(data);
412
-
413
- const tableHeadList = $("th").map((i, el) => ({
414
- headingRow: i,
415
- text: $(el).text()
416
- })).toArray();
417
-
418
- results(tableHeadList);
419
- } catch (err) { failed(err); }
420
- };
421
-
422
- const table_data = async ({results, failed}) => {
423
- try {
424
- const { data } = await scraping.get(url);
425
- $ = cheerio.load(data);
426
-
427
- const tableColumnList = $("td").map((i, el) => $(el).text()).toArray();
428
-
429
- results(tableColumnList);
430
- } catch (err) { failed(err); }
431
- };
432
-
433
-
434
- const links = async ({results, failed}) => {
435
- try {
436
- const { data } = await scraping.get(url);
437
- $ = cheerio.load(data);
438
-
439
- const linkList = $("a").map((i, el) => ({
440
- url: $(el).attr("href"),
441
- text: $(el).text()
442
- })).toArray()
443
- .filter(({ url }) => url.indexOf("#") !== 0);
444
-
445
- results(linkList);
446
- } catch (err) { failed(err); }
447
- };
448
-
449
- const cites = async ({results, failed}) => {
450
- try {
451
- const { data } = await scraping.get(url);
452
- $ = cheerio.load(data);
453
-
454
- const citeList = $("q, blockquote").map((i, el) => ({
455
- citeTag: $(el).prop("tagName"),
456
- citeLink: $(el).attr("cite"),
457
- citeText: $(el).text()
458
- })).toArray();
459
-
460
- results(citeList);
461
- } catch (err) { failed(err); }
462
- };
463
-
464
- return {
465
- title,
466
- images,
467
- metadata,
468
- headings,
469
- table_heading,
470
- table_data,
471
- links,
472
- cites
473
- };
337
+ async function scrape({url, options, results, failed}) {
338
+ let scrapingResult;
339
+
340
+ try {
341
+ const { data } = await axios.get(url);
342
+ const $ = load(data);
343
+
344
+ const scraping = {
345
+ title() {
346
+ results($("title").text());
347
+ },
348
+ images() {
349
+ const imageList = $("img").map((i, el) => ({
350
+ imagePath: $(el).attr("src"),
351
+ imageTitle: $(el).attr("alt")
352
+ })).toArray();
353
+
354
+ scrapingResult = imageList.length === 0
355
+ ? "no found images"
356
+ : imageList;
357
+
358
+ results(scrapingResult);
359
+ },
360
+ metadata() {
361
+ const metadataList = $("meta").map((i, el) => ({
362
+ metaInfo: $(el).attr("name"),
363
+ metaContent: $(el).attr("content")
364
+ })).toArray()
365
+ .filter((data) => data?.metaInfo);
366
+
367
+ results(metadataList);
368
+ },
369
+ headings() {
370
+ const headingList = $("h1, h2, h3, h4, h5, h6").map((i, el) => ({
371
+ headingTag: $(el).prop("tagName"),
372
+ headingText: $(el).text()
373
+ })).toArray();
374
+
375
+ results(headingList);
376
+ },
377
+ tableHead() {
378
+ const tableHeadList = $("th").map((i, el) => ({
379
+ headingRow: i,
380
+ text: $(el).text()
381
+ })).toArray();
382
+
383
+ scrapingResult = tableHeadList.length === 0
384
+ ? "no found th tags" : tableHeadList;
385
+
386
+ results(scrapingResult);
387
+ },
388
+ tableData() {
389
+ const tableColumnList = $("td").map((i, el) => ({
390
+ tableRow: i + 1,
391
+ tableData: $(el).text(),
392
+ })).toArray();
393
+
394
+ scrapingResult = tableColumnList.length === 0
395
+ ? "no found td tags" : tableColumnList;
396
+
397
+ results(scrapingResult);
398
+ },
399
+ links() {
400
+ const linkList = $("a").map((i, el) => ({
401
+ url: $(el).attr("href"),
402
+ text: $(el).text()
403
+ })).toArray()
404
+ .filter(({ url }) => url.indexOf("#") !== 0);
405
+
406
+ results(linkList);
407
+ },
408
+ cites() {
409
+ const citeList = $("q, blockquote").map((i, el) => ({
410
+ citeTag: $(el).prop("tagName"),
411
+ citeLink: $(el).attr("cite"),
412
+ citeText: $(el).text()
413
+ })).toArray();
414
+
415
+ scrapingResult = citeList.length === 0
416
+ ? "no found q and/or blockquote tags" : citeList;
417
+
418
+ results(scrapingResult);
419
+ }
420
+ };
421
+
422
+ scraping[options]();
423
+ } catch (err) {
424
+ failed(err.message);
425
+ }
474
426
  }
475
427
 
476
428
  const password = () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stack-analyze",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "type": "module",
5
5
  "description": "cli tech stack analyze and pagespeed with node.js using the wappalyzer module. with google pagespeed api, hardware and crypto market",
6
6
  "main": "index.mjs",
@@ -14,23 +14,23 @@
14
14
  }
15
15
  },
16
16
  "dependencies": {
17
- "axios": "^0.27.2",
17
+ "axios": "^1.1.3",
18
18
  "cheerio": "^1.0.0-rc.12",
19
19
  "cli-progress": "^3.11.2",
20
20
  "colors": "^1.4.0",
21
21
  "console-table-printer": "^2.11.1",
22
22
  "figlet": "^1.5.2",
23
- "inquirer": "^9.1.1",
24
- "systeminformation": "^5.12.6",
23
+ "gauge": "^5.0.0",
24
+ "inquirer": "^9.1.4",
25
+ "systeminformation": "^5.12.11",
25
26
  "timeago.js": "^4.0.2",
26
- "wappalyzer": "^6.10.39"
27
+ "wappalyzer": "^6.10.41"
27
28
  },
28
29
  "devDependencies": {
29
- "eslint": "^8.23.0",
30
+ "eslint": "^8.26.0",
30
31
  "gh-pages": "^4.0.0",
31
- "jest": "^29.0.1",
32
- "jsdoc": "^3.6.11",
33
- "minami": "^1.2.3"
32
+ "jest": "^29.2.2",
33
+ "jsdoc": "^3.6.11"
34
34
  },
35
35
  "scripts": {
36
36
  "start": "node cli.js",
package/scraping.json ADDED
@@ -0,0 +1,6 @@
1
+ [
2
+ {
3
+ "metaInfo": "viewport",
4
+ "metaContent": "width=device-width, initial-scale=1"
5
+ }
6
+ ]
package/utils.js CHANGED
@@ -1,15 +1,89 @@
1
+ import { writeFile } from "node:fs/promises";
2
+
1
3
  const listFormat = new Intl.ListFormat("en", {
2
4
  style: "short",
3
5
  type: "conjunction"
4
6
  });
5
7
 
6
- const currency = Intl.NumberFormat("en-us", {
7
- style: "currency",
8
- currency: "USD"
8
+ const currency = new Intl.NumberFormat("en-us", {
9
+ style: "currency", currency: "USD"
9
10
  });
10
11
 
12
+ const returnMainOpts = "return main menu";
13
+
14
+ const menuOpts = [
15
+ "web", "info", "query", "utility", "about", "exit"
16
+ ];
17
+
18
+ const menuWebOpts = [
19
+ "single", "multiple", "pagespeed", "scraping", returnMainOpts
20
+ ];
21
+
22
+ const menuInfoOpts = [
23
+ "github_info", "crypto_market", "bitly_info", returnMainOpts
24
+ ];
25
+
26
+ const menuQueryOpts = [
27
+ "anime_Search", "movie_info", "twitch_info", returnMainOpts
28
+ ];
29
+
30
+ const menuUtilityOpts = [
31
+ "hardware", "password", returnMainOpts
32
+ ];
33
+
34
+ const menuHardwareOpts = [
35
+ "cpuInfo", "ramMemInfo", "osDetail", "diskInfo",
36
+ "controllerInfo", "displayInfo", "biosInfo", returnMainOpts
37
+ ];
38
+
39
+ const menuAboutOpts = [
40
+ "mainInfo", "lineup", "youtubeRecomendation",
41
+ "twitchRecomendation", "projectsRecomendation", returnMainOpts
42
+ ];
43
+
44
+ const scrapingOpts = [
45
+ "title", "images", "metadata", "headings",
46
+ "tableHead", "tableData", "links", "cites"
47
+ ];
48
+
49
+ /**
50
+ *
51
+ * @param {string} filename
52
+ * @param {any} data
53
+ * @returns {Promise<void>}
54
+ */
55
+ const stackSave = async (filename, data) => {
56
+ if (!data) {
57
+ console.error("stackSave no using falsy values");
58
+ return;
59
+ }
60
+
61
+ if(typeof data === "boolean") {
62
+ console.info("stackSave no using boolean types");
63
+ return;
64
+ }
65
+
66
+ try {
67
+ await writeFile(filename, data);
68
+ } catch (err) {
69
+ console.info(err.message);
70
+ }
71
+ };
72
+
73
+ const exitCli = "thanks for use stack-analyze";
74
+
11
75
  export {
12
76
  listFormat,
13
- currency
77
+ currency,
78
+ menuOpts,
79
+ menuWebOpts,
80
+ menuInfoOpts,
81
+ menuQueryOpts,
82
+ menuUtilityOpts,
83
+ menuHardwareOpts,
84
+ menuAboutOpts,
85
+ scrapingOpts,
86
+ stackSave,
87
+ exitCli
14
88
  };
15
89
 
@@ -0,0 +1,37 @@
1
+ // bitly regexp
2
+ const bitlyRegexp = /bit\.ly\//g;
3
+
4
+ const bitlyQuery = {
5
+ name: "bitlyLink",
6
+ message: "enter a short link:",
7
+ validate: input => bitlyRegexp.test(input) || "only bitly link".yellow
8
+ };
9
+
10
+ /**
11
+ * @param {string} name
12
+ * @param {string} message
13
+ */
14
+ const promptParams = (name, message) => ({
15
+ name,
16
+ message,
17
+ validate: input => input !== "" || "this field is required".yellow
18
+ });
19
+
20
+ /**
21
+ * @param {string} name
22
+ * @param {string} message
23
+ */
24
+ const promptKey = (name, message) => ({
25
+ name,
26
+ message,
27
+ type: "password",
28
+ mask: "?",
29
+ validate: input => input !== "" || "token field is required".yellow
30
+ });
31
+
32
+ export {
33
+ bitlyQuery,
34
+ promptParams,
35
+ promptKey
36
+ };
37
+
@@ -0,0 +1,33 @@
1
+ import { scrapingOpts } from "../utils.js";
2
+
3
+ const webRegex = /https?:\/\//g;
4
+
5
+ const singleWebQuery = {
6
+ name: "url",
7
+ message: "enter a url:",
8
+ validate: input => webRegex.test(input) || "enter a url valid".yellow
9
+ };
10
+
11
+ const multipleWebQuery = {
12
+ name: "webList",
13
+ message: "enter URLs for analyze the tech stacks with whitespace without quotes example 'http://example.com https://nodejs.org': \n",
14
+ validate(input) {
15
+ const pass = input.match(webRegex);
16
+
17
+ return pass && pass.length === 2 || "must be 2 sites";
18
+ }
19
+ };
20
+
21
+ const webScrapingQuery = {
22
+ type: "list",
23
+ pageSize: 9,
24
+ name: "option",
25
+ message: "select a web scraping option:",
26
+ choices: scrapingOpts
27
+ };
28
+
29
+ export {
30
+ singleWebQuery,
31
+ multipleWebQuery,
32
+ webScrapingQuery
33
+ };