stack-analyze 1.0.85 → 1.0.110
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/about.js +24 -0
- package/changelog.md +14 -0
- package/dramaqueen.js +32 -0
- package/functions/animeInfo.js +40 -0
- package/functions/lyrics.js +24 -0
- package/functions/pokemonInfo.js +32 -0
- package/functions/techStack.js +46 -0
- package/image/logo.png +0 -0
- package/image/menu.png +0 -0
- package/index.js +116 -120
- package/models/animeTable.js +33 -0
- package/models/stackTables.js +23 -0
- package/package.json +18 -31
- package/readme.md +16 -18
- package/utils/format.js +1 -0
- package/CHANGELOG.md +0 -59
- package/LICENSE +0 -21
- package/about/index.js +0 -22
- package/docs/logo-module.png +0 -0
- package/functions/multipleStack.js +0 -43
- package/functions/pageSpeed.js +0 -132
- package/functions/singleStack.js +0 -34
package/about.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const { version } = require('./package.json')
|
|
2
|
+
const formatter = require('./utils/format.js')
|
|
3
|
+
|
|
4
|
+
const devs = ['omega5300']
|
|
5
|
+
const keyPeoples = ['dannyaegyo', 'angel amor de danny']
|
|
6
|
+
const dannyLinkList = [
|
|
7
|
+
'instagram',
|
|
8
|
+
'patreon',
|
|
9
|
+
'twitch',
|
|
10
|
+
'tiktok',
|
|
11
|
+
'facebook',
|
|
12
|
+
'twitter'
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
const aboutApp = {
|
|
16
|
+
stackAnalyzeFounder: 'omega5300',
|
|
17
|
+
version,
|
|
18
|
+
dramalandia_leader: keyPeoples[0],
|
|
19
|
+
key_peoples: formatter.format(keyPeoples),
|
|
20
|
+
dannyaegyo_links: formatter.format(dannyLinkList),
|
|
21
|
+
developers: formatter.format(devs),
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = aboutApp
|
package/changelog.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# changelog
|
|
2
|
+
|
|
3
|
+
todos las versiones de la nueva rama de stack-analyze
|
|
4
|
+
|
|
5
|
+
## version 1.0.110 (1.0.11)
|
|
6
|
+
- new feature:
|
|
7
|
+
- lyrics finder from stack-analyze delta
|
|
8
|
+
- move to new repo
|
|
9
|
+
## version 1.0.10
|
|
10
|
+
- primera version bajo la rama de dramalandia.
|
|
11
|
+
- que posee algunas de la primera generación son:
|
|
12
|
+
- tech-stack
|
|
13
|
+
- anime search
|
|
14
|
+
- pokemon info
|
package/dramaqueen.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module.exports = `
|
|
2
|
+
::::::::::::::::::::::::::::::tGGGGC0;::;8GGGGC:::::::::::::
|
|
3
|
+
:::::::::::::::::::::::::::::10GGG080;:180GGGG;:::::::::::::
|
|
4
|
+
:::::::::::::::::::::::::::::G00G0000:1880GGL:::::::::::::::
|
|
5
|
+
::::::::::::::::::::::::::::;00G1000Li0800L:::;;::::::::::::
|
|
6
|
+
:::::::::::::::::::::::::::::00;;00810000i::;:::::::::::::::
|
|
7
|
+
:::::::::::::::::::::::::::::f:1L00CC00C::::::::::::::::::::
|
|
8
|
+
::::::::::::::::::::::::;::;1GGG088888i:::::::::::::::::::::
|
|
9
|
+
::::::::::::::::::::::,:;1;:10GGG008800i::::::::::::::::::::
|
|
10
|
+
::::::::::::::::::::::,::iitG800GGG00000i:::::::::::::::::::
|
|
11
|
+
::::::::::::::::::::::,,,,;GLCLftGGGGGGGf:::::::::::::::::::
|
|
12
|
+
::::::::::::::::::::::,,,i01::;;ifLG800GC:::::::::::::::::::
|
|
13
|
+
::::::::::::::::::::..:;G001:ii:;:;1L80Gf:::::::::::::::::::
|
|
14
|
+
::::::::::::::::::,.if:0000t1i:;::;L8000f:::::::::::::::::::
|
|
15
|
+
:::::::::::::::::;:,,;00800Gii1i1L@88000t:::::::::::::::::::
|
|
16
|
+
:::::::::::::::;:,,,:0000088GCG,:C8088L1i;::::::::::::::::::
|
|
17
|
+
::::::::::::::;:,,,:f8888880G80;f88888t;i:::::::::::::::::::
|
|
18
|
+
::::::::::::;;:,,,,:880000000888888888t:::ii::::::::::::::::
|
|
19
|
+
::::::::::;L;:,,,,,f880000t::;t88088080;,,:,,:::::::::::::::
|
|
20
|
+
::::::::::Ci::,,,,i888080C:,;::iG00000881;,:::::::::::::::::
|
|
21
|
+
::::::::::LC;,,:t00888008i::::;iG8888808L;::::::::::::::::::
|
|
22
|
+
:::::::::::;tCGCf;:880008G:,;;:iG0G0000@t,.,;:::::::::::::::
|
|
23
|
+
::::::::::::::::::G88000000000888000008@L:.,::::::::::::::::
|
|
24
|
+
::::::::::::::::::888880000008880G08888@C;::;:::::::::::::::
|
|
25
|
+
::::::::::::::::::::t8888888888880080088t;:;;:::::::::::::::
|
|
26
|
+
:::::::::::::::::::::C888008888888888L@Ci;:;i:::::::::::::::
|
|
27
|
+
:::::::::::::::::::::t88888880808888f:fCi;;ii:::::::::::::::
|
|
28
|
+
:::::::::::::::::::::0888808808888881::fC1ii;:::::::::::::::
|
|
29
|
+
:::::::::::::::::::i88888000888888888i:::;;:::::::::::::::::
|
|
30
|
+
:::::::::::::::::;08888888888888888888t:::::::::::::::::::::
|
|
31
|
+
::::::::::::::::iGG88888888888888888888i::::::::::::::::::::
|
|
32
|
+
`;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const axios = require('axios')
|
|
2
|
+
const { format } = require('timeago.js')
|
|
3
|
+
const { red } = require('colors')
|
|
4
|
+
const { printTable } = require('console-table-printer')
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
*
|
|
8
|
+
* @description call the anime serach info
|
|
9
|
+
* @param { string } query - get query results
|
|
10
|
+
* @returns { Promise<void> } - return results serach
|
|
11
|
+
*
|
|
12
|
+
*/
|
|
13
|
+
async function animeSearch (query) {
|
|
14
|
+
try {
|
|
15
|
+
const { data } = await axios.get('https://api.jikan.moe/v3/search/anime', {
|
|
16
|
+
params: {
|
|
17
|
+
q: query,
|
|
18
|
+
limit: 10
|
|
19
|
+
}
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
const animeData = data.results.map(({
|
|
23
|
+
title,
|
|
24
|
+
episodes,
|
|
25
|
+
start_date,
|
|
26
|
+
end_date,
|
|
27
|
+
type
|
|
28
|
+
}) => ({
|
|
29
|
+
title,
|
|
30
|
+
type,
|
|
31
|
+
episodes,
|
|
32
|
+
debutDate: format(start_date),
|
|
33
|
+
finalDate: end_date === null ? "current date" : format(end_date)
|
|
34
|
+
}))
|
|
35
|
+
|
|
36
|
+
printTable(animeData)
|
|
37
|
+
} catch(err) { console.error(red(err.message)) }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = animeSearch
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const fs = require('node:fs/promises')
|
|
2
|
+
const lyricsFinder = require('lyrics-finder')
|
|
3
|
+
const { red } = require('colors')
|
|
4
|
+
|
|
5
|
+
const lyricSearch = async (artist, title) => {
|
|
6
|
+
const $date = new Date()
|
|
7
|
+
const dateFormat = new Intl.DateTimeFormat('es', {
|
|
8
|
+
dateStyle: 'short',
|
|
9
|
+
timeStyle: 'short',
|
|
10
|
+
hour12: true,
|
|
11
|
+
timeZone: 'America/Mexico_City'
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
const lyrics = await lyricsFinder(artist, title) || 'Not Found!'
|
|
15
|
+
|
|
16
|
+
if(lyrics !== 'Not Found!') {
|
|
17
|
+
await fs.writeFile(`${artist} - ${title}.txt`, lyrics)
|
|
18
|
+
console.info(`descargar finaliza: ${dateFormat.format($date)}`.green)
|
|
19
|
+
} else {
|
|
20
|
+
console.error(red(lyrics))
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = lyricSearch
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const axios = require('axios')
|
|
2
|
+
const { red } = require('colors')
|
|
3
|
+
|
|
4
|
+
const formatter = require('../utils/format.js')
|
|
5
|
+
|
|
6
|
+
const pokemonInfo = async (pokemon) => {
|
|
7
|
+
try {
|
|
8
|
+
const { data } = await axios.get(`https://pokeapi.co/api/v2/pokemon/${pokemon}`)
|
|
9
|
+
|
|
10
|
+
const { id, name } = data
|
|
11
|
+
|
|
12
|
+
const moveList = data.moves.map(move => move.move.name).slice(0, 10)
|
|
13
|
+
const typeList = data.types.map(type => type.type.name).slice(0, 10)
|
|
14
|
+
|
|
15
|
+
console.table({
|
|
16
|
+
id,
|
|
17
|
+
name,
|
|
18
|
+
hp: data.stats[0].base_stat,
|
|
19
|
+
attack: data.stats[1].base_stat,
|
|
20
|
+
defense: data.stats[2].base_stat,
|
|
21
|
+
special_attack: data.stats[3].base_stat,
|
|
22
|
+
special_defense: data.stats[4].base_stat,
|
|
23
|
+
speed: data.stats[5].base_stat,
|
|
24
|
+
types: formatter.format(typeList),
|
|
25
|
+
moves: formatter.format(moveList)
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
moves = moveList
|
|
29
|
+
} catch (err) { console.error(red(err)) }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports = pokemonInfo
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const Wappalyzer = require('wappalyzer')
|
|
2
|
+
const { textSync } = require('figlet')
|
|
3
|
+
const { red, green } = require('colors')
|
|
4
|
+
|
|
5
|
+
const stackTable = require("../models/stackTables");
|
|
6
|
+
|
|
7
|
+
const formatter = require('../utils/format.js')
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
*
|
|
11
|
+
* @description call single website tech stack analyze
|
|
12
|
+
* @param { string } url - analyze single website stack
|
|
13
|
+
* @returns { Promise<void> } - return async results single web
|
|
14
|
+
*
|
|
15
|
+
*/
|
|
16
|
+
async function techStack(url) {
|
|
17
|
+
const wappalyzer = await new Wappalyzer()
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
await wappalyzer.init()
|
|
21
|
+
|
|
22
|
+
const { technologies } = await wappalyzer.open(url).analyze()
|
|
23
|
+
|
|
24
|
+
const results = technologies.map(app => {
|
|
25
|
+
const webCategories = app.categories.map(({ name }) => name)
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
techName: app.name,
|
|
29
|
+
techWebsite: app.website,
|
|
30
|
+
techCategories: formatter.format(webCategories)
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
console.info(green(textSync(url)))
|
|
35
|
+
|
|
36
|
+
stackTable.addRows(results)
|
|
37
|
+
|
|
38
|
+
stackTable.printTable()
|
|
39
|
+
} catch (err) {
|
|
40
|
+
console.error(red(err.message))
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
await wappalyzer.destroy()
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
module.exports = techStack
|
package/image/logo.png
ADDED
|
Binary file
|
package/image/menu.png
ADDED
|
Binary file
|
package/index.js
CHANGED
|
@@ -1,40 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// modules
|
|
4
|
-
const
|
|
5
|
-
const
|
|
4
|
+
const { performance } = require('perf_hooks')
|
|
5
|
+
const inquirer = require('inquirer')
|
|
6
|
+
const { textSync } = require('figlet')
|
|
7
|
+
const { red, brightMagenta } = require('colors')
|
|
8
|
+
const { printTable } = require('console-table-printer')
|
|
6
9
|
|
|
7
|
-
//
|
|
8
|
-
const aboutApp = require(
|
|
10
|
+
// about
|
|
11
|
+
const aboutApp = require('./about')
|
|
9
12
|
|
|
10
|
-
//
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
+
// options modules
|
|
14
|
+
const TechStack = require("./functions/techStack")
|
|
15
|
+
const animeSearch = require("./functions/animeInfo")
|
|
16
|
+
const pokemonInfo = require('./functions/pokemonInfo')
|
|
13
17
|
|
|
14
|
-
|
|
15
|
-
const { desktop, mobile } = require("./functions/pageSpeed");
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
*
|
|
19
|
-
* @description call the function question raw list options
|
|
20
|
-
* @return { void }
|
|
21
|
-
*
|
|
22
|
-
*/
|
|
23
|
-
function question() {
|
|
24
|
-
inquirer.prompt({
|
|
25
|
-
type: "rawlist",
|
|
26
|
-
name: "analyze",
|
|
27
|
-
message: "what option do you want to analyze stack",
|
|
28
|
-
choices: [
|
|
29
|
-
"single",
|
|
30
|
-
"multiple",
|
|
31
|
-
"page speed",
|
|
32
|
-
"about",
|
|
33
|
-
"exit"
|
|
34
|
-
]
|
|
35
|
-
})
|
|
36
|
-
.then((anw) => anwOption(anw.analyze));
|
|
37
|
-
}
|
|
18
|
+
const image = require('./dramaqueen')
|
|
38
19
|
|
|
39
20
|
/**
|
|
40
21
|
*
|
|
@@ -43,99 +24,114 @@ function question() {
|
|
|
43
24
|
*
|
|
44
25
|
*/
|
|
45
26
|
async function returnQuestion() {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
anw.return
|
|
55
|
-
? question()
|
|
56
|
-
: console.log("\x1b[44mthanks for use stack-analyze\x1b[0m");
|
|
57
|
-
}
|
|
27
|
+
try {
|
|
28
|
+
const anw = await inquirer.prompt([
|
|
29
|
+
{
|
|
30
|
+
type: "confirm",
|
|
31
|
+
name: "return",
|
|
32
|
+
message: "¿Deseas regresar al menú principal?",
|
|
33
|
+
}
|
|
34
|
+
]);
|
|
58
35
|
|
|
59
|
-
|
|
60
|
-
*
|
|
61
|
-
* @description function register option anwser
|
|
62
|
-
* @param { string } result - result option anwser
|
|
63
|
-
* @return { void }
|
|
64
|
-
*
|
|
65
|
-
*/
|
|
66
|
-
function anwOption(result) {
|
|
67
|
-
// options conditional
|
|
68
|
-
switch (result) {
|
|
69
|
-
case "single":
|
|
70
|
-
console.clear();
|
|
71
|
-
inquirer.prompt({
|
|
72
|
-
name: "url",
|
|
73
|
-
message: "enter url for analyze the tech stack:"
|
|
74
|
-
})
|
|
75
|
-
.then((anw) => {
|
|
76
|
-
if (anw.url.indexOf("http" || "https") > -1) {
|
|
77
|
-
singleStack(anw.url);
|
|
78
|
-
setTimeout(returnQuestion, 83000);
|
|
79
|
-
} else {
|
|
80
|
-
console.error("\x1b[31mplease insert a URL with parameter http:// or https://");
|
|
81
|
-
question();
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
break;
|
|
85
|
-
case "multiple":
|
|
86
|
-
console.clear();
|
|
87
|
-
inquirer.prompt({
|
|
88
|
-
name: "urls",
|
|
89
|
-
message: "enter URLs for analyze the tech stacks with whitespace without quotes example 'http://example.com https://nodejs.org': \n"
|
|
90
|
-
})
|
|
91
|
-
.then((anw) => {
|
|
92
|
-
if (
|
|
93
|
-
anw.urls.match(/(http|https)/g) !== null ||
|
|
94
|
-
anw.urls.match(/(http|https)/g) === 2
|
|
95
|
-
) {
|
|
96
|
-
const websites = anw.urls.split(" ");
|
|
97
|
-
console.clear();
|
|
98
|
-
multipleStack(websites);
|
|
99
|
-
setTimeout(returnQuestion, 83000);
|
|
100
|
-
} else {
|
|
101
|
-
console.error("\x1b[31mplease in each URL insert a website the parameter https:// or http://");
|
|
102
|
-
question();
|
|
103
|
-
}
|
|
104
|
-
});
|
|
105
|
-
break;
|
|
106
|
-
case "page speed":
|
|
107
|
-
console.clear();
|
|
108
|
-
inquirer.prompt({
|
|
109
|
-
name: "speedWeb",
|
|
110
|
-
message: "insert URL for page speed analyze:"
|
|
111
|
-
})
|
|
112
|
-
.then((anw) => {
|
|
113
|
-
if (anw.speedWeb.indexOf("http" || "https") > -1) {
|
|
114
|
-
console.clear();
|
|
115
|
-
textSync(anw.speedWeb, "Small");
|
|
116
|
-
mobile(anw.speedWeb);
|
|
117
|
-
desktop(anw.speedWeb);
|
|
118
|
-
setTimeout(returnQuestion, 83000);
|
|
119
|
-
} else {
|
|
120
|
-
console.error("\x1b[31mplease insert a URL with parameter https;// or http://");
|
|
121
|
-
question();
|
|
122
|
-
}
|
|
123
|
-
});
|
|
124
|
-
break;
|
|
125
|
-
case "about":
|
|
126
|
-
// about info cli
|
|
36
|
+
if (anw.return) {
|
|
127
37
|
console.clear();
|
|
128
|
-
console.table(aboutApp);
|
|
129
38
|
question();
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
console.
|
|
133
|
-
|
|
39
|
+
} else {
|
|
40
|
+
console.clear();
|
|
41
|
+
console.info("Gracias por usar stack-analyze dramalandia".green);
|
|
42
|
+
}
|
|
43
|
+
} catch (err) {
|
|
44
|
+
console.error(red(err.message));
|
|
134
45
|
}
|
|
135
46
|
}
|
|
136
47
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
48
|
+
/**
|
|
49
|
+
@description This is a hash table with the options of the tools menu.
|
|
50
|
+
@type {{ single(): void, multiple(): void, pagespeed(): void, github_info(): void, anime_search(): void, crypto_market(): void, bitly_info(): void, movie_info(): void, twitch_info(): void }}
|
|
51
|
+
*/
|
|
52
|
+
const toolsOpts = {
|
|
53
|
+
tech_stack() {
|
|
54
|
+
console.clear();
|
|
55
|
+
inquirer.prompt({
|
|
56
|
+
name: "url",
|
|
57
|
+
message: "Ingrese el link para analizar que tecnologías tiene:"
|
|
58
|
+
}).then(({ url }) => {
|
|
59
|
+
if (url.indexOf("http") === 0) {
|
|
60
|
+
TechStack(url);
|
|
61
|
+
const timeEnd = performance.now();
|
|
62
|
+
setTimeout(returnQuestion, timeEnd);
|
|
63
|
+
} else {
|
|
64
|
+
console.error("Por favor ingrese el link con http:// o https://".red);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
},
|
|
68
|
+
anime_search() {
|
|
69
|
+
console.clear();
|
|
70
|
+
inquirer.prompt({
|
|
71
|
+
name: "anime",
|
|
72
|
+
message: "Ingrese el anime, película u ova para buscar"
|
|
73
|
+
}).then(({ anime }) => {
|
|
74
|
+
if (anime !== "") {
|
|
75
|
+
console.clear();
|
|
76
|
+
animeSearch(anime);
|
|
77
|
+
setTimeout(returnQuestion, 2000);
|
|
78
|
+
} else {
|
|
79
|
+
console.error("Por favor el anime es obligatorio".red);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
},
|
|
83
|
+
pokemon_info() {
|
|
84
|
+
console.clear();
|
|
85
|
+
inquirer.prompt({
|
|
86
|
+
name: "pokemon",
|
|
87
|
+
message: "Ingrese el nombre o ID de Pokémon para buscar"
|
|
88
|
+
}).then(({ pokemon }) => {
|
|
89
|
+
if (pokemon !== "" || pokemon !== 0) {
|
|
90
|
+
console.clear();
|
|
91
|
+
pokemonInfo(pokemon);
|
|
92
|
+
|
|
93
|
+
setTimeout(returnQuestion, 2000);
|
|
94
|
+
} else {
|
|
95
|
+
console.error("Por favor el id o el nombre de Pokémon es obligatorio".red);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
},
|
|
99
|
+
drama_queen() {
|
|
100
|
+
console.clear()
|
|
101
|
+
console.info('danny la reina de drama')
|
|
102
|
+
console.info(image)
|
|
103
|
+
returnQuestion()
|
|
104
|
+
},
|
|
105
|
+
about() {
|
|
106
|
+
console.clear()
|
|
107
|
+
console.table(aboutApp)
|
|
108
|
+
returnQuestion()
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
function question() {
|
|
114
|
+
console.clear()
|
|
115
|
+
console.info(brightMagenta(textSync('dramalandia')))
|
|
116
|
+
console.info('Software de stack-analyze para la comunidad de dramalandia'.brightYellow)
|
|
117
|
+
inquirer.prompt({
|
|
118
|
+
type: 'list',
|
|
119
|
+
name: 'opts',
|
|
120
|
+
message: 'Seleccione la herramienta a usar:',
|
|
121
|
+
choices: [
|
|
122
|
+
'tech_stack',
|
|
123
|
+
'anime_search',
|
|
124
|
+
'pokemon_info',
|
|
125
|
+
'drama_queen',
|
|
126
|
+
'about',
|
|
127
|
+
'exit'
|
|
128
|
+
]
|
|
129
|
+
}).then(({ opts }) => {
|
|
130
|
+
if (opts === 'exit') {
|
|
131
|
+
console.clear()
|
|
132
|
+
console.info('Gracias por usar stack-analyze dramalandia'.green)
|
|
133
|
+
} else { toolsOpts[opts](); }
|
|
134
|
+
})
|
|
135
|
+
}
|
|
141
136
|
|
|
137
|
+
question()
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const { Table } = require("console-table-printer")
|
|
2
|
+
|
|
3
|
+
const animeList = new Table({
|
|
4
|
+
columns: [
|
|
5
|
+
{
|
|
6
|
+
name: "title",
|
|
7
|
+
alignment: "left",
|
|
8
|
+
color: "green"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
name: "type",
|
|
12
|
+
alignment: "left",
|
|
13
|
+
color: "magenta"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
name: "episodes",
|
|
17
|
+
alignment: "left",
|
|
18
|
+
color: "magenta"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
name: "debutDate",
|
|
22
|
+
alignment: "left",
|
|
23
|
+
color: "magenta"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: "finalDate",
|
|
27
|
+
alignment: "left",
|
|
28
|
+
color: "green"
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
module.exports = animeList
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const { Table } = require("console-table-printer")
|
|
2
|
+
|
|
3
|
+
const stackTable = new Table({
|
|
4
|
+
columns: [
|
|
5
|
+
{
|
|
6
|
+
name: "techName",
|
|
7
|
+
alignment: "left",
|
|
8
|
+
color: "cyan"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
name: "techWebsite",
|
|
12
|
+
alignment: "left",
|
|
13
|
+
color: "green"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
name: "techCategories",
|
|
17
|
+
alignment: "left",
|
|
18
|
+
color: "cyan"
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
module.exports = stackTable
|
package/package.json
CHANGED
|
@@ -1,45 +1,32 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stack-analyze",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.0.110",
|
|
4
|
+
"description": "edicion de stack-analyze para la comunidad de dannyaegyo",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"stack-
|
|
7
|
+
"stack-dramalandia": "index.js"
|
|
8
8
|
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"start": "node index.js"
|
|
11
|
+
},
|
|
12
|
+
"author": "omega5300",
|
|
13
|
+
"license": "MIT",
|
|
9
14
|
"dependencies": {
|
|
10
|
-
"axios": "^0.
|
|
11
|
-
"cli-progress": "^3.8.2",
|
|
15
|
+
"axios": "^0.27.2",
|
|
12
16
|
"colors": "^1.4.0",
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"
|
|
19
|
-
"jest": "^26.6.2"
|
|
20
|
-
},
|
|
21
|
-
"scripts": {
|
|
22
|
-
"start": "node index.js",
|
|
23
|
-
"test": "jest",
|
|
24
|
-
"lint:test": "eslint . --ext .js",
|
|
25
|
-
"lint:fix": "eslint . --ext .js --fix"
|
|
17
|
+
"console-table-printer": "^2.11.0",
|
|
18
|
+
"figlet": "^1.5.2",
|
|
19
|
+
"inquirer": "^8.2.4",
|
|
20
|
+
"lyrics-finder": "^21.7.0",
|
|
21
|
+
"timeago.js": "^4.0.2",
|
|
22
|
+
"wappalyzer": "^6.10.26"
|
|
26
23
|
},
|
|
27
24
|
"repository": {
|
|
28
25
|
"type": "git",
|
|
29
|
-
"url": "git+https://github.com/
|
|
26
|
+
"url": "git+https://github.com/stack-analyze/stack-analyze-dramalandia.git"
|
|
30
27
|
},
|
|
31
|
-
"keywords": [
|
|
32
|
-
"cli",
|
|
33
|
-
"tech stack",
|
|
34
|
-
"intermachine",
|
|
35
|
-
"stack-analyze",
|
|
36
|
-
"pagespeed analyze",
|
|
37
|
-
"ascii art"
|
|
38
|
-
],
|
|
39
|
-
"author": "Intermachine Developers",
|
|
40
|
-
"license": "MIT",
|
|
41
28
|
"bugs": {
|
|
42
|
-
"url": "https://github.com/
|
|
29
|
+
"url": "https://github.com/stack-analyze/stack-analyze-dramalandia/issues"
|
|
43
30
|
},
|
|
44
|
-
"homepage": "https://github.com/
|
|
31
|
+
"homepage": "https://github.com/stack-analyze/stack-analyze-dramalandia#readme"
|
|
45
32
|
}
|
package/readme.md
CHANGED
|
@@ -1,27 +1,25 @@
|
|
|
1
|
-
# stack
|
|
1
|
+
# stack-analyze dramalandia
|
|
2
2
|
|
|
3
|
-

|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
[](https://stackshare.io/intermachine-developers/stack-analyze-cli)
|
|
7
|
-
[](https://badge.fury.io/js/stack-analyze)
|
|
8
|
-
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
sotware de consola enfocado para la comunidad dramalandia del dannyaegyo es una version simplificada de la primera generacion de stack-analyze (1.0.4 a 1.0.9) siendo el primer CLI enfocado a los fanaticos de anime.
|
|
9
6
|
|
|
10
|
-
cli tech stack analyze with **node.js** using the wappalyzer and google pagespeed api the module this node external module or install.
|
|
11
7
|
|
|
12
|
-
|
|
8
|
+
## uso
|
|
13
9
|
|
|
14
|
-
|
|
15
|
-
>npx stack-analyze "using external without install"<br>
|
|
16
|
-
>npm i -g stack-analyze "global install"<br>
|
|
17
|
-
>note: if global install fail using npx
|
|
10
|
+
el uso es tanto instalacion global como uso portable
|
|
18
11
|
|
|
19
|
-
|
|
12
|
+
``` sh
|
|
13
|
+
# npm 8.10 inferior
|
|
14
|
+
npm i -g stack-analyze@dramalandia
|
|
20
15
|
|
|
21
|
-
|
|
16
|
+
# npm 8.12 superior
|
|
17
|
+
npm i --location=global stack-analyze@dramalandia
|
|
22
18
|
|
|
23
|
-
|
|
24
|
-
-
|
|
25
|
-
|
|
19
|
+
# uso portable
|
|
20
|
+
npx stack-analyze@dramalandia
|
|
21
|
+
```
|
|
26
22
|
|
|
27
|
-
|
|
23
|
+
## ejemplo
|
|
24
|
+
|
|
25
|
+

|
package/utils/format.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = new Intl.ListFormat('en', { style: 'short', type: 'conjunction' })
|
package/CHANGELOG.md
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
# changelog
|
|
2
|
-
|
|
3
|
-
stack-analyze all version and notable changes, fixed, remove and new additions in code.
|
|
4
|
-
|
|
5
|
-
## version 1.0.8
|
|
6
|
-
### Added
|
|
7
|
-
- add the new modules:
|
|
8
|
-
- colors
|
|
9
|
-
- cli-progress
|
|
10
|
-
### fixed
|
|
11
|
-
change json print to table print version
|
|
12
|
-
### Changed
|
|
13
|
-
- change console.dir about to console.table
|
|
14
|
-
- change from text to bar for pagespeed results
|
|
15
|
-
- add the badge tech stack project
|
|
16
|
-
### patch version 1.0.85
|
|
17
|
-
notes:
|
|
18
|
-
- fixed the cli run
|
|
19
|
-
- remove performance.now for the defined time in pagespeed
|
|
20
|
-
- add recomendations sections
|
|
21
|
-
|
|
22
|
-
## version 1.0.7
|
|
23
|
-
### Added
|
|
24
|
-
- add the new modules:
|
|
25
|
-
- figlet the main module
|
|
26
|
-
- jest dev module and testing functions
|
|
27
|
-
- add the options page speed and about the main options
|
|
28
|
-
- add return in a option select except about and exit
|
|
29
|
-
### Fixed
|
|
30
|
-
- the website in blank or website without http:// or https:// return the main options
|
|
31
|
-
- eslint custom rules without style guide
|
|
32
|
-
### Changed
|
|
33
|
-
- changed the inquirer list to inquirer rawlist
|
|
34
|
-
- rewirte jsdoc in all project except test files
|
|
35
|
-
- the tech stack using other console methods console.log only in exit cli
|
|
36
|
-
|
|
37
|
-
## version 1.0.6
|
|
38
|
-
### Added
|
|
39
|
-
- add the new options: multiple (analyze many sites) and exit (exit cli).
|
|
40
|
-
- the code was testing with eslint to avoid errors to execute
|
|
41
|
-
- rewrite the module docs.
|
|
42
|
-
### Changed
|
|
43
|
-
- the list options with inquirer
|
|
44
|
-
- welcome message modified
|
|
45
|
-
|
|
46
|
-
## version 1.0.5
|
|
47
|
-
### Changed
|
|
48
|
-
- modify the code and module structure
|
|
49
|
-
### remove
|
|
50
|
-
- remove the commander module
|
|
51
|
-
|
|
52
|
-
## version 1.0.4
|
|
53
|
-
- fisrt official version in npm with the modules:
|
|
54
|
-
- wappalyzer
|
|
55
|
-
- inquirer
|
|
56
|
-
- commander
|
|
57
|
-
|
|
58
|
-
## version 1.0.1 to 1.0.3
|
|
59
|
-
test version modules without wappalyzer module (only testing)
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2020 julian david cordoba torres
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
package/about/index.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
// version module
|
|
2
|
-
const { version } = require("../package.json");
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* about data object
|
|
7
|
-
* @typedef { Object } aboutApp - structure info app
|
|
8
|
-
* @property { string } aboutApp.mainDeveloper - leader cli app developers
|
|
9
|
-
* @property { string[] } aboutApp.developers - developers author the cli app
|
|
10
|
-
* @property { string[] } aboutApp.devRecommendationYoutube - youtubers recomendation from omega5300
|
|
11
|
-
* @property { string } aboutApp.version - version the cli app
|
|
12
|
-
*/
|
|
13
|
-
const aboutApp = {
|
|
14
|
-
mainDeveloper: "omega5300",
|
|
15
|
-
developers: ["omega5300"].join(", "),
|
|
16
|
-
devRecommendationYoutube: ["fazt", "doriandesings", "bluuweb", "leonidas esteban"].join(", "),
|
|
17
|
-
nonoliveStreamersRecommendation: ["⚔️GothspiceChann💰"].join(", "),
|
|
18
|
-
projectsRecommendation: ["Doofy's Projects"].join(", "),
|
|
19
|
-
version
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
module.exports = aboutApp;
|
package/docs/logo-module.png
DELETED
|
Binary file
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
const { textSync } = require("figlet");
|
|
2
|
-
const Wappalyzer = require("wappalyzer");
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
*
|
|
6
|
-
* @description call multiple websites tech stack analyze
|
|
7
|
-
* @param { string[] } urls - tech analyze in multiples websites
|
|
8
|
-
* @returns { Promise<void> } - async results in multiples websites
|
|
9
|
-
*
|
|
10
|
-
*/
|
|
11
|
-
const multipleStack = async (urls) => {
|
|
12
|
-
const wappalyzer = await new Wappalyzer();
|
|
13
|
-
|
|
14
|
-
try {
|
|
15
|
-
await wappalyzer.init();
|
|
16
|
-
|
|
17
|
-
const results = await Promise.all(
|
|
18
|
-
urls.map(async (url) => ({
|
|
19
|
-
url,
|
|
20
|
-
stack: await wappalyzer.open(url).analyze()
|
|
21
|
-
})));
|
|
22
|
-
|
|
23
|
-
console.info("multiple websites tech stack \n");
|
|
24
|
-
console.group();
|
|
25
|
-
// loop web site tech stack
|
|
26
|
-
for (const result of results) {
|
|
27
|
-
console.info("\x1b[36m", textSync(result.url, "Small"), "\x1b[0m");
|
|
28
|
-
console.group();
|
|
29
|
-
console.table(result.stack.technologies.map((app) => ({
|
|
30
|
-
"tech-name": app.name,
|
|
31
|
-
"tech-website": app.website,
|
|
32
|
-
"tech-categories": Object.values(app.categories).map((categorie) => categorie.name).join(", ")
|
|
33
|
-
})));
|
|
34
|
-
console.groupEnd();
|
|
35
|
-
}
|
|
36
|
-
} catch (err) {
|
|
37
|
-
console.error("\x1b[31m", err.message, "\x1b[0m");
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
await wappalyzer.destroy();
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
module.exports = multipleStack;
|
package/functions/pageSpeed.js
DELETED
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
// modules
|
|
2
|
-
const axios = require("axios").default;
|
|
3
|
-
const colors = require("colors");
|
|
4
|
-
const cliProgress = require("cli-progress");
|
|
5
|
-
|
|
6
|
-
// result pagespeed bar color
|
|
7
|
-
let bar;
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* @description async function mobile website pagespeed
|
|
11
|
-
* @param { string } url - website from pagespeed mobile results
|
|
12
|
-
* @returns { Promise<void> } - return async mobile results
|
|
13
|
-
*/
|
|
14
|
-
const mobile = async (url) => {
|
|
15
|
-
const res = await axios.get(
|
|
16
|
-
`https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=${url}&key=AIzaSyBEDaW4FxSZ2s1vz5CdD5Ai6PGZGdAzij0&strategy=mobile`
|
|
17
|
-
);
|
|
18
|
-
|
|
19
|
-
try {
|
|
20
|
-
const movil = res.data.lighthouseResult.categories.performance.score * 100;
|
|
21
|
-
|
|
22
|
-
switch (true) {
|
|
23
|
-
case (movil === 1 || movil <= 49):
|
|
24
|
-
bar = new cliProgress.SingleBar({
|
|
25
|
-
format: `Mobile Result | ${colors.red("{bar}")} || {value}/{total} || bad`,
|
|
26
|
-
barCompleteChar: "\u2588",
|
|
27
|
-
barIncompleteChar: "\u2591",
|
|
28
|
-
hideCursor: true
|
|
29
|
-
});
|
|
30
|
-
break;
|
|
31
|
-
case (movil === 50 || movil <= 89):
|
|
32
|
-
bar = new cliProgress.SingleBar({
|
|
33
|
-
format: `Mobile Result | ${colors.yellow("{bar}")} || {value}/{total} decent`,
|
|
34
|
-
barCompleteChar: "\u2588",
|
|
35
|
-
barIncompleteChar: "\u2591",
|
|
36
|
-
hideCursor: true
|
|
37
|
-
});
|
|
38
|
-
break;
|
|
39
|
-
case (movil >= 90 || movil === 100):
|
|
40
|
-
bar = new cliProgress.SingleBar({
|
|
41
|
-
format: `Mobile Result | ${colors.green("{bar}")} || {value}/{total} excelent`,
|
|
42
|
-
barCompleteChar: "\u2588",
|
|
43
|
-
barIncompleteChar: "\u2591",
|
|
44
|
-
hideCursor: true
|
|
45
|
-
});
|
|
46
|
-
break;
|
|
47
|
-
default:
|
|
48
|
-
bar = new cliProgress.SingleBar({
|
|
49
|
-
format: `Mobile Result | ${colors.green("{bar}")} || {value}/{total} excelent`,
|
|
50
|
-
barCompleteChar: "\u2588",
|
|
51
|
-
barIncompleteChar: "\u2591",
|
|
52
|
-
hideCursor: true
|
|
53
|
-
});
|
|
54
|
-
break;
|
|
55
|
-
}
|
|
56
|
-
// initial bar
|
|
57
|
-
bar.start(100, 0);
|
|
58
|
-
|
|
59
|
-
// update values
|
|
60
|
-
bar.update(Math.round(movil));
|
|
61
|
-
|
|
62
|
-
bar.stop();
|
|
63
|
-
} catch (err) {
|
|
64
|
-
console.error(err.message);
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
*
|
|
70
|
-
* @description async function desktop website pagespeed
|
|
71
|
-
* @param { string } url - website from pagespeed desktop
|
|
72
|
-
* @return { Promise<void> } - return async desktop results
|
|
73
|
-
*
|
|
74
|
-
*/
|
|
75
|
-
const desktop = async (url) => {
|
|
76
|
-
const res = await axios.get(
|
|
77
|
-
`https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=${url}&key=AIzaSyBEDaW4FxSZ2s1vz5CdD5Ai6PGZGdAzij0&strategy=desktop`
|
|
78
|
-
);
|
|
79
|
-
|
|
80
|
-
try {
|
|
81
|
-
const desktop = res.data.lighthouseResult.categories.performance.score * 100;
|
|
82
|
-
|
|
83
|
-
switch (true) {
|
|
84
|
-
case (desktop === 0 || desktop <=49):
|
|
85
|
-
bar = new cliProgress.SingleBar({
|
|
86
|
-
format: `Mobile Result | ${colors.red("{bar}")} || {value}/{total} || bad`,
|
|
87
|
-
barCompleteChar: "\u2588",
|
|
88
|
-
barIncompleteChar: "\u2591",
|
|
89
|
-
hideCursor: true
|
|
90
|
-
});
|
|
91
|
-
break;
|
|
92
|
-
case (desktop === 50 || desktop <=89):
|
|
93
|
-
bar = new cliProgress.SingleBar({
|
|
94
|
-
format: `Mobile Result | ${colors.yellow("{bar}")} || {value}/{total} decent`,
|
|
95
|
-
barCompleteChar: "\u2588",
|
|
96
|
-
barIncompleteChar: "\u2591",
|
|
97
|
-
hideCursor: true
|
|
98
|
-
});
|
|
99
|
-
break;
|
|
100
|
-
case (desktop >=90 || desktop === 100):
|
|
101
|
-
bar = new cliProgress.SingleBar({
|
|
102
|
-
format: `Mobile Result | ${colors.green("{bar}")} || {value}/{total} excelent`,
|
|
103
|
-
barCompleteChar: "\u2588",
|
|
104
|
-
barIncompleteChar: "\u2591",
|
|
105
|
-
hideCursor: true
|
|
106
|
-
});
|
|
107
|
-
break;
|
|
108
|
-
default:
|
|
109
|
-
bar = new cliProgress.SingleBar({
|
|
110
|
-
format: `Mobile Result | ${colors.magenta("{bar}")} || {value}/{total} undifined`,
|
|
111
|
-
barCompleteChar: "\u2588",
|
|
112
|
-
barIncomopleteChar: "\u2591",
|
|
113
|
-
hideCursor: true
|
|
114
|
-
});
|
|
115
|
-
break;
|
|
116
|
-
}
|
|
117
|
-
// initial bar
|
|
118
|
-
bar.start(100, 0);
|
|
119
|
-
|
|
120
|
-
// update values
|
|
121
|
-
bar.update(Math.round(desktop));
|
|
122
|
-
|
|
123
|
-
bar.stop();
|
|
124
|
-
} catch (err) {
|
|
125
|
-
console.error("\x1b[31m", err.message, "\x1b[0m");
|
|
126
|
-
}
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
module.exports = {
|
|
130
|
-
mobile,
|
|
131
|
-
desktop
|
|
132
|
-
};
|
package/functions/singleStack.js
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
// module
|
|
2
|
-
const Wappalyzer = require("wappalyzer");
|
|
3
|
-
const { textSync } = require("figlet");
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
*
|
|
7
|
-
* @description call single website tech stack analyze
|
|
8
|
-
* @param { string } url - analyze single website stack
|
|
9
|
-
* @returns { Promise<void> } - return async results single web
|
|
10
|
-
*
|
|
11
|
-
*/
|
|
12
|
-
async function singleStack (url) {
|
|
13
|
-
const wappalyzer = await new Wappalyzer;
|
|
14
|
-
|
|
15
|
-
try {
|
|
16
|
-
await wappalyzer.init();
|
|
17
|
-
|
|
18
|
-
const results = await wappalyzer.open(url).analyze();
|
|
19
|
-
|
|
20
|
-
console.info("\x1b[32m", textSync(url), "\x1b[0m");
|
|
21
|
-
|
|
22
|
-
console.table(results.technologies.map((app) => ({
|
|
23
|
-
"tech-name": app.name,
|
|
24
|
-
"tech-website": app.website,
|
|
25
|
-
"tech-categories": Object.values(app.categories).map((categorie) => categorie.name).join(", ")
|
|
26
|
-
})));
|
|
27
|
-
} catch (err) {
|
|
28
|
-
console.error("\x1b[31m", err.message, "\x1b[0m");
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
await wappalyzer.destroy();
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
module.exports = singleStack;
|