mdsmith 1.1.2 β 1.2.0
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/README-MDSMITH.md +23 -11
- package/bin/index.js +712 -107
- package/mdsmith.config.json +2 -1
- package/package.json +4 -1
package/README-MDSMITH.md
CHANGED
|
@@ -1,30 +1,42 @@
|
|
|
1
1
|
|
|
2
|
-
#
|
|
2
|
+
# mdsmith
|
|
3
3
|
|
|
4
4
|
CLI para gerar READMEs e arquivos Markdown
|
|
5
5
|
|
|
6
|
-
## π
|
|
7
|
-
- **
|
|
8
|
-
- **
|
|
9
|
-
- **
|
|
6
|
+
## π InformaΓ§Γ΅es do Projeto
|
|
7
|
+
- **VersΓ£o:** 1.1.2
|
|
8
|
+
- **Gerenciador de Pacotes:** npm
|
|
9
|
+
- **Tipo:** Node.js Project
|
|
10
10
|
|
|
11
|
-
## π Dependencies
|
|
12
|
-
No dependencies found.
|
|
13
11
|
|
|
14
12
|
|
|
15
|
-
## π
|
|
13
|
+
## π Estrutura do Projeto
|
|
16
14
|
```
|
|
17
15
|
π bin
|
|
18
16
|
π beta.js
|
|
19
17
|
π index.js
|
|
20
18
|
π mdsmith.config.json
|
|
19
|
+
π package-lock.json
|
|
21
20
|
π package.json
|
|
22
21
|
π README-MDSMITH.md
|
|
23
22
|
|
|
24
23
|
```
|
|
25
24
|
|
|
26
|
-
## π Available Scripts
|
|
27
|
-
No scripts available.
|
|
28
25
|
|
|
26
|
+
## π Getting Started
|
|
29
27
|
|
|
30
|
-
|
|
28
|
+
```bash
|
|
29
|
+
npm install
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
Gerado por **mdSmith**
|
package/bin/index.js
CHANGED
|
@@ -5,18 +5,86 @@
|
|
|
5
5
|
// npx mdsmith --tree # show project structure
|
|
6
6
|
// npx mdsmith --deps # list dependencies
|
|
7
7
|
// npx mdsmith --readme # generate README
|
|
8
|
+
// npx mdsmith --analyze # project analyze
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
const TECH_MAP = {
|
|
12
|
+
// Frameworks
|
|
13
|
+
express: "Express",
|
|
14
|
+
fastify: "Fastify",
|
|
15
|
+
koa: "Koa",
|
|
16
|
+
hapi: "Hapi",
|
|
17
|
+
"@nestjs/core": "NestJS",
|
|
18
|
+
|
|
19
|
+
// ORMs
|
|
20
|
+
prisma: "Prisma",
|
|
21
|
+
"@prisma/client": "Prisma",
|
|
22
|
+
sequelize: "Sequelize",
|
|
23
|
+
mongoose: "Mongoose",
|
|
24
|
+
typeorm: "TypeORM",
|
|
25
|
+
drizzle: "Drizzle ORM",
|
|
26
|
+
|
|
27
|
+
// Databases
|
|
28
|
+
pg: "PostgreSQL",
|
|
29
|
+
mysql: "MySQL",
|
|
30
|
+
mysql2: "MySQL",
|
|
31
|
+
sqlite3: "SQLite",
|
|
32
|
+
mongodb: "MongoDB",
|
|
33
|
+
|
|
34
|
+
// Auth
|
|
35
|
+
jsonwebtoken: "JWT",
|
|
36
|
+
passport: "Passport",
|
|
37
|
+
|
|
38
|
+
// AI
|
|
39
|
+
openai: "OpenAI",
|
|
40
|
+
langchain: "LangChain",
|
|
41
|
+
|
|
42
|
+
// Infra / Tools
|
|
43
|
+
docker: "Docker",
|
|
44
|
+
"swagger-ui-express": "Swagger",
|
|
45
|
+
puppeteer: "Puppeteer",
|
|
46
|
+
nodemailer: "Nodemailer"
|
|
47
|
+
}
|
|
48
|
+
|
|
8
49
|
|
|
50
|
+
const i18n = {
|
|
51
|
+
en: {
|
|
52
|
+
projectInfo: "Project Info",
|
|
53
|
+
dependencies: "Dependencies",
|
|
54
|
+
projectStructure: "Project Structure",
|
|
55
|
+
availableScripts: "Available Scripts",
|
|
56
|
+
version: "Version",
|
|
57
|
+
packageManager: "Package Manager",
|
|
58
|
+
type: "Type",
|
|
59
|
+
generatedBy: "Generated by"
|
|
60
|
+
},
|
|
61
|
+
pt: {
|
|
62
|
+
projectInfo: "InformaΓ§Γ΅es do Projeto",
|
|
63
|
+
dependencies: "DependΓͺncias",
|
|
64
|
+
projectStructure: "Estrutura do Projeto",
|
|
65
|
+
availableScripts: "Scripts DisponΓveis",
|
|
66
|
+
version: "VersΓ£o",
|
|
67
|
+
packageManager: "Gerenciador de Pacotes",
|
|
68
|
+
type: "Tipo",
|
|
69
|
+
generatedBy: "Gerado por"
|
|
70
|
+
},
|
|
71
|
+
es: {
|
|
72
|
+
projectInfo: "InformaciΓ³n del Proyecto",
|
|
73
|
+
dependencies: "Dependencias",
|
|
74
|
+
projectStructure: "Estructura del Proyecto",
|
|
75
|
+
availableScripts: "Scripts Disponibles",
|
|
76
|
+
version: "VersiΓ³n",
|
|
77
|
+
packageManager: "Administrador de Paquetes",
|
|
78
|
+
type: "Tipo",
|
|
79
|
+
generatedBy: "Generado por"
|
|
80
|
+
}
|
|
81
|
+
}
|
|
9
82
|
|
|
10
83
|
const fs = require("fs");
|
|
11
84
|
|
|
12
85
|
const path = require("path");
|
|
13
86
|
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
const rl = readline.createInterface({
|
|
17
|
-
input: process.stdin,
|
|
18
|
-
output: process.stdout
|
|
19
|
-
});
|
|
87
|
+
const prompts = require('prompts');
|
|
20
88
|
|
|
21
89
|
const projectRoot = process.cwd();
|
|
22
90
|
|
|
@@ -34,7 +102,49 @@ const args = process.argv.slice(2)
|
|
|
34
102
|
|
|
35
103
|
const defaultConfig = {
|
|
36
104
|
ignore: ["node_modules", ".git", ".vscode"],
|
|
37
|
-
depth: Infinity
|
|
105
|
+
depth: Infinity,
|
|
106
|
+
emojis: true
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function healthChecks(packageJson, projectRoot, analysis) {
|
|
110
|
+
const checks = []
|
|
111
|
+
|
|
112
|
+
if (!analysis.testing) {
|
|
113
|
+
checks.push({
|
|
114
|
+
type: "warning",
|
|
115
|
+
message: "No test runner configured."
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (!analysis.linting) {
|
|
120
|
+
checks.push({
|
|
121
|
+
type: "info",
|
|
122
|
+
message: "No ESLint detected."
|
|
123
|
+
})
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (!analysis.formatting) {
|
|
127
|
+
checks.push({
|
|
128
|
+
type: "info",
|
|
129
|
+
message: "No Prettier detected."
|
|
130
|
+
})
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (!packageJson.engines?.node) {
|
|
134
|
+
checks.push({
|
|
135
|
+
type: "critical",
|
|
136
|
+
message: "Node version not defined in package.json engines."
|
|
137
|
+
})
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (!fs.existsSync(path.join(projectRoot, "Dockerfile"))) {
|
|
141
|
+
checks.push({
|
|
142
|
+
type: "info",
|
|
143
|
+
message: "No Dockerfile found."
|
|
144
|
+
})
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return checks
|
|
38
148
|
}
|
|
39
149
|
|
|
40
150
|
|
|
@@ -88,80 +198,58 @@ function scanDir(dirPath, padding, config) {
|
|
|
88
198
|
return treeContent
|
|
89
199
|
}
|
|
90
200
|
|
|
91
|
-
function
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
Project name not found in package.json.
|
|
105
|
-
Would you like to provide one? (y/n): `)
|
|
106
|
-
resposta = resposta.trim().toLowerCase()
|
|
107
|
-
|
|
108
|
-
if(resposta == "y" || resposta == "n"){
|
|
109
|
-
respostaValida = true
|
|
110
|
-
}else {
|
|
111
|
-
console.log("Please type y or n.")
|
|
112
|
-
}
|
|
113
|
-
}
|
|
201
|
+
async function buscarNome(nome) {
|
|
202
|
+
if (!nome) {
|
|
203
|
+
const confirm = await prompts({
|
|
204
|
+
type: 'confirm',
|
|
205
|
+
name: 'value',
|
|
206
|
+
message: 'Project name not found in package.json. Provide one?',
|
|
207
|
+
initial: true
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
if (!confirm.value) {
|
|
211
|
+
console.log("\nProject name will be set as Unnamed Project.\n");
|
|
212
|
+
return "Unnamed Project";
|
|
213
|
+
}
|
|
114
214
|
|
|
215
|
+
const response = await prompts({
|
|
216
|
+
type: 'text',
|
|
217
|
+
name: 'value',
|
|
218
|
+
message: 'Enter the project name:'
|
|
219
|
+
});
|
|
115
220
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}if(resposta == "y"){
|
|
120
|
-
let nome = await perguntar("Enter the project name: ")
|
|
121
|
-
console.log(`Project name set to "${nome}".`)
|
|
122
|
-
return nome
|
|
123
|
-
}else{
|
|
124
|
-
console.error("Failed to generate project name.")
|
|
125
|
-
process.exit(1)
|
|
126
|
-
}
|
|
127
|
-
}
|
|
221
|
+
console.log(`\nProject name set to "${response.value}".\n`);
|
|
222
|
+
return response.value || "Unnamed Project";
|
|
223
|
+
}
|
|
128
224
|
|
|
129
|
-
|
|
225
|
+
return nome;
|
|
130
226
|
}
|
|
131
227
|
|
|
132
|
-
async function buscarDescricao(descricao){
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}else {
|
|
146
|
-
console.log("Please type y or n.")
|
|
147
|
-
}
|
|
148
|
-
}
|
|
228
|
+
async function buscarDescricao(descricao) {
|
|
229
|
+
if (!descricao) {
|
|
230
|
+
const confirm = await prompts({
|
|
231
|
+
type: 'confirm',
|
|
232
|
+
name: 'value',
|
|
233
|
+
message: 'Project description not found. Provide one?',
|
|
234
|
+
initial: true
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
if (!confirm.value) {
|
|
238
|
+
console.log("\nDescription placeholder added.\n");
|
|
239
|
+
return "*Project description goes here*";
|
|
240
|
+
}
|
|
149
241
|
|
|
242
|
+
const response = await prompts({
|
|
243
|
+
type: 'text',
|
|
244
|
+
name: 'value',
|
|
245
|
+
message: 'Enter the project description:'
|
|
246
|
+
});
|
|
150
247
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}if(resposta == "y"){
|
|
155
|
-
let descricao = await perguntar("Enter the project description: ")
|
|
156
|
-
console.log("\nDescription saved.\n")
|
|
157
|
-
return descricao
|
|
158
|
-
}else{
|
|
159
|
-
console.error("Failed to generate project description.")
|
|
160
|
-
process.exit(1)
|
|
161
|
-
}
|
|
162
|
-
}
|
|
248
|
+
console.log("\nDescription saved.\n");
|
|
249
|
+
return response.value || "*Project description goes here*";
|
|
250
|
+
}
|
|
163
251
|
|
|
164
|
-
|
|
252
|
+
return descricao;
|
|
165
253
|
}
|
|
166
254
|
|
|
167
255
|
function detectarTipoProjeto(packageJson) {
|
|
@@ -188,40 +276,330 @@ function formatScripts(scripts) {
|
|
|
188
276
|
.join("\n");
|
|
189
277
|
}
|
|
190
278
|
|
|
279
|
+
function detectarEnvVars(projectRoot) {
|
|
280
|
+
const envPath = path.join(projectRoot, ".env")
|
|
281
|
+
|
|
282
|
+
if (!fs.existsSync(envPath)) return []
|
|
283
|
+
|
|
284
|
+
const content = fs.readFileSync(envPath, "utf-8")
|
|
285
|
+
|
|
286
|
+
const vars = content
|
|
287
|
+
.split("\n")
|
|
288
|
+
.map(line => line.trim())
|
|
289
|
+
.filter(line =>
|
|
290
|
+
line &&
|
|
291
|
+
!line.startsWith("#") &&
|
|
292
|
+
line.includes("=")
|
|
293
|
+
)
|
|
294
|
+
.map(line => line.split("=")[0].trim())
|
|
295
|
+
|
|
296
|
+
return vars
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function gerarEnvSection(vars) {
|
|
300
|
+
if (!vars.length) return ""
|
|
301
|
+
|
|
302
|
+
let section = "## π Environment Variables\n\n"
|
|
303
|
+
section += "Create a `.env` file in the project root with the following variables:\n\n"
|
|
304
|
+
|
|
305
|
+
vars.forEach(v => {
|
|
306
|
+
section += `- ${v}\n`
|
|
307
|
+
})
|
|
308
|
+
|
|
309
|
+
return section + "\n"
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function detectarPorta(scanData) {
|
|
313
|
+
const filesToScan = [...scanData.jsFiles, ...scanData.tsFiles]
|
|
314
|
+
|
|
315
|
+
for (const filePath of filesToScan) {
|
|
316
|
+
try {
|
|
317
|
+
const content = fs.readFileSync(filePath, "utf-8")
|
|
318
|
+
|
|
319
|
+
// π procurar qualquer .listen(...)
|
|
320
|
+
const listenRegex = /\.listen\s*\(\s*([^)]+)\)/
|
|
321
|
+
const listenMatch = content.match(listenRegex)
|
|
322
|
+
|
|
323
|
+
if (!listenMatch) continue
|
|
324
|
+
|
|
325
|
+
const rawArgument = listenMatch[1].split(",")[0].trim()
|
|
326
|
+
|
|
327
|
+
// =============================
|
|
328
|
+
// β
1οΈβ£ NΓΊmero direto
|
|
329
|
+
// =============================
|
|
330
|
+
if (/^\d+$/.test(rawArgument)) {
|
|
331
|
+
return rawArgument
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// =============================
|
|
335
|
+
// β
2οΈβ£ Fallback inline
|
|
336
|
+
// ex: process.env.PORT || 3000
|
|
337
|
+
// =============================
|
|
338
|
+
const inlineFallback = rawArgument.match(/\|\|\s*(\d+)/)
|
|
339
|
+
if (inlineFallback) {
|
|
340
|
+
return inlineFallback[1]
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// =============================
|
|
344
|
+
// β
3οΈβ£ VariΓ‘vel
|
|
345
|
+
// ex: app.listen(port)
|
|
346
|
+
// =============================
|
|
347
|
+
const variableName = rawArgument.replace(/[^a-zA-Z0-9_]/g, "")
|
|
348
|
+
|
|
349
|
+
if (!variableName) continue
|
|
350
|
+
|
|
351
|
+
const variableRegex = new RegExp(
|
|
352
|
+
`(const|let|var)\\s+${variableName}\\s*=\\s*([^;]+)`
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
const variableMatch = content.match(variableRegex)
|
|
356
|
+
|
|
357
|
+
if (variableMatch) {
|
|
358
|
+
const variableValue = variableMatch[2]
|
|
359
|
+
|
|
360
|
+
// fallback dentro da variΓ‘vel
|
|
361
|
+
const fallbackMatch = variableValue.match(/\|\|\s*(\d+)/)
|
|
362
|
+
if (fallbackMatch) {
|
|
363
|
+
return fallbackMatch[1]
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// nΓΊmero direto dentro da variΓ‘vel
|
|
367
|
+
const numberMatch = variableValue.match(/\d+/)
|
|
368
|
+
if (numberMatch) {
|
|
369
|
+
return numberMatch[0]
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// =============================
|
|
374
|
+
// β
4οΈβ£ process.env.PORT puro
|
|
375
|
+
// =============================
|
|
376
|
+
if (rawArgument.includes("process.env.PORT")) {
|
|
377
|
+
return "process.env.PORT"
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
} catch (err) {
|
|
381
|
+
// evita quebrar o CLI se algum arquivo falhar
|
|
382
|
+
continue
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
return null
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
function scanProjectFiles(projectRoot) {
|
|
390
|
+
const result = {
|
|
391
|
+
allFiles: [],
|
|
392
|
+
jsFiles: [],
|
|
393
|
+
tsFiles: [],
|
|
394
|
+
envFiles: [],
|
|
395
|
+
hasDockerfile: false,
|
|
396
|
+
hasPrisma: false,
|
|
397
|
+
hasSrcFolder: false,
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function walk(dir) {
|
|
401
|
+
const files = fs.readdirSync(dir)
|
|
402
|
+
|
|
403
|
+
for (const file of files) {
|
|
404
|
+
const fullPath = path.join(dir, file)
|
|
405
|
+
|
|
406
|
+
// ignorar node_modules e .git
|
|
407
|
+
if (
|
|
408
|
+
fullPath.includes("node_modules") ||
|
|
409
|
+
fullPath.includes(".git")
|
|
410
|
+
) continue
|
|
411
|
+
|
|
412
|
+
const stat = fs.statSync(fullPath)
|
|
413
|
+
|
|
414
|
+
if (stat.isDirectory()) {
|
|
415
|
+
if (file === "src") {
|
|
416
|
+
result.hasSrcFolder = true
|
|
417
|
+
}
|
|
418
|
+
walk(fullPath)
|
|
419
|
+
} else {
|
|
420
|
+
result.allFiles.push(fullPath)
|
|
421
|
+
|
|
422
|
+
if (file.endsWith(".js")) result.jsFiles.push(fullPath)
|
|
423
|
+
if (file.endsWith(".ts")) result.tsFiles.push(fullPath)
|
|
424
|
+
if (file.startsWith(".env")) result.envFiles.push(fullPath)
|
|
425
|
+
|
|
426
|
+
if (file === "Dockerfile") result.hasDockerfile = true
|
|
427
|
+
if (fullPath.includes("prisma")) result.hasPrisma = true
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
walk(projectRoot)
|
|
433
|
+
|
|
434
|
+
return result
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function gerarGettingStarted(packageManager = "npm", scripts = {}, hasPrisma) {
|
|
438
|
+
const lines = []
|
|
439
|
+
|
|
440
|
+
lines.push(
|
|
441
|
+
packageManager === "npm"
|
|
442
|
+
? "npm install"
|
|
443
|
+
: `${packageManager} install`
|
|
444
|
+
)
|
|
445
|
+
|
|
446
|
+
if (hasPrisma) {
|
|
447
|
+
lines.push("npx prisma migrate dev")
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
if (scripts.dev) {
|
|
451
|
+
lines.push(getRunCommand("dev", packageManager))
|
|
452
|
+
} else if (scripts.start) {
|
|
453
|
+
lines.push(getRunCommand("start", packageManager))
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
return `
|
|
457
|
+
## π Getting Started
|
|
458
|
+
|
|
459
|
+
\`\`\`bash
|
|
460
|
+
${lines.join("\n")}
|
|
461
|
+
\`\`\`
|
|
462
|
+
|
|
463
|
+
`
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
function getRunCommand(script, packageManager) {
|
|
468
|
+
if (packageManager === "yarn") return `yarn ${script}`
|
|
469
|
+
if (packageManager === "pnpm") return `pnpm ${script}`
|
|
470
|
+
return `npm run ${script}`
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
async function generateReadme(packageJson, tree, config){
|
|
476
|
+
|
|
477
|
+
const deps = {
|
|
478
|
+
...packageJson.dependencies,
|
|
479
|
+
...packageJson.devDependencies
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
const techStack = detectarTechStack(deps)
|
|
483
|
+
const techSection = gerarTechStackSection(techStack)
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
const envVars = detectarEnvVars(projectRoot)
|
|
489
|
+
const envSection = gerarEnvSection(envVars)
|
|
490
|
+
|
|
491
|
+
const nomeProjeto = await buscarNome(packageJson.name);
|
|
492
|
+
const descricaoProjeto = await buscarDescricao(packageJson.description);
|
|
493
|
+
const idioma = await escolherLingua()
|
|
494
|
+
const t = i18n[idioma] || i18n.en
|
|
495
|
+
|
|
496
|
+
const packageManager = detectarPackageManager(projectRoot)
|
|
497
|
+
|
|
498
|
+
const scanData = scanProjectFiles(projectRoot)
|
|
499
|
+
const port = detectarPorta(scanData)
|
|
500
|
+
|
|
501
|
+
const commands = analisarScripts(packageJson.scripts, packageManager)
|
|
502
|
+
|
|
503
|
+
const hasPrisma = detectarPrisma(projectRoot, packageJson)
|
|
504
|
+
const dbSection = hasPrisma ? gerarDatabaseSection(packageManager) : ""
|
|
505
|
+
|
|
506
|
+
const gettingStartedCommands = gerarGettingStarted(
|
|
507
|
+
packageManager,
|
|
508
|
+
packageJson.scripts,
|
|
509
|
+
hasPrisma
|
|
510
|
+
)
|
|
191
511
|
|
|
192
|
-
async function generateReadme(packageJson, tree){
|
|
193
512
|
|
|
194
|
-
const nomeProjeto = await buscarNome(packageJson.name);
|
|
195
|
-
const descricaoProjeto = await buscarDescricao(packageJson.description);
|
|
196
513
|
|
|
197
514
|
const content = `
|
|
198
515
|
# ${nomeProjeto}
|
|
199
516
|
|
|
200
517
|
${descricaoProjeto}
|
|
201
518
|
|
|
202
|
-
## π
|
|
203
|
-
-
|
|
204
|
-
-
|
|
205
|
-
-
|
|
519
|
+
## ${config.emojis ? "π" : ""} ${t.projectInfo}
|
|
520
|
+
- **${t.version}:** ${packageJson.version || "Not specified"}
|
|
521
|
+
- **${t.packageManager}:** ${packageManager}
|
|
522
|
+
- **${t.type}:** ${detectarTipoProjeto(packageJson)}
|
|
206
523
|
|
|
207
|
-
|
|
208
|
-
${formatDependencies(packageJson.dependencies)}
|
|
524
|
+
${techSection}
|
|
209
525
|
|
|
210
|
-
## π
|
|
526
|
+
## ${config.emojis ? "π" : ""} ${t.projectStructure}
|
|
211
527
|
\`\`\`
|
|
212
528
|
${tree}
|
|
213
529
|
\`\`\`
|
|
214
530
|
|
|
215
|
-
|
|
216
|
-
|
|
531
|
+
${gettingStartedCommands}
|
|
532
|
+
|
|
533
|
+
${envSection}
|
|
534
|
+
${dbSection}
|
|
217
535
|
|
|
218
|
-
|
|
536
|
+
${port ? `
|
|
537
|
+
## π Server
|
|
538
|
+
|
|
539
|
+
After starting, the server will run at:
|
|
540
|
+
|
|
541
|
+
http://localhost:${port}
|
|
542
|
+
|
|
543
|
+
` : ""}
|
|
544
|
+
|
|
545
|
+
|
|
546
|
+
---
|
|
547
|
+
|
|
548
|
+
${t.generatedBy} **mdSmith**
|
|
219
549
|
`
|
|
220
550
|
|
|
221
551
|
return content
|
|
222
552
|
|
|
223
553
|
}
|
|
224
554
|
|
|
555
|
+
function detectarPrisma(projectRoot, packageJson) {
|
|
556
|
+
const deps = {
|
|
557
|
+
...packageJson.dependencies,
|
|
558
|
+
...packageJson.devDependencies
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
const hasPrismaDep = deps.prisma || deps["@prisma/client"]
|
|
562
|
+
const hasPrismaFolder = fs.existsSync(path.join(projectRoot, "prisma"))
|
|
563
|
+
|
|
564
|
+
return hasPrismaDep && hasPrismaFolder
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
function gerarDatabaseSection(packageManager) {
|
|
568
|
+
let section = "## π Database Setup\n\n"
|
|
569
|
+
section += "Run Prisma migrations:\n\n"
|
|
570
|
+
section += "```bash\n"
|
|
571
|
+
|
|
572
|
+
if (packageManager === "npm") {
|
|
573
|
+
section += "npx prisma migrate dev\n"
|
|
574
|
+
section += "npx prisma generate\n"
|
|
575
|
+
} else {
|
|
576
|
+
section += `${packageManager} prisma migrate dev\n`
|
|
577
|
+
section += `${packageManager} prisma generate\n`
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
section += "```\n\n"
|
|
581
|
+
|
|
582
|
+
return section
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
|
|
587
|
+
async function escolherLingua() {
|
|
588
|
+
const response = await prompts({
|
|
589
|
+
type: 'select',
|
|
590
|
+
name: 'lang',
|
|
591
|
+
message: 'Choose README language',
|
|
592
|
+
choices: [
|
|
593
|
+
{ title: 'English', value: 'en' },
|
|
594
|
+
{ title: 'PortuguΓͺs', value: 'pt' },
|
|
595
|
+
{ title: 'EspaΓ±ol', value: 'es' }
|
|
596
|
+
],
|
|
597
|
+
initial: 0
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
return response.lang || 'en';
|
|
601
|
+
}
|
|
602
|
+
|
|
225
603
|
function loadConfig(projectRoot) {
|
|
226
604
|
const configPath = path.join(projectRoot, "mdsmith.config.json")
|
|
227
605
|
|
|
@@ -249,6 +627,209 @@ function loadConfig(projectRoot) {
|
|
|
249
627
|
}
|
|
250
628
|
}
|
|
251
629
|
|
|
630
|
+
function analisarProjeto(packageJson, projectRoot) {
|
|
631
|
+
const deps = {
|
|
632
|
+
...packageJson.dependencies,
|
|
633
|
+
...packageJson.devDependencies
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
const analysis = {
|
|
637
|
+
frontend: deps.react ? "React" :
|
|
638
|
+
deps.next ? "Next.js" :
|
|
639
|
+
null,
|
|
640
|
+
|
|
641
|
+
backend: deps.express ? "Express" :
|
|
642
|
+
deps.fastify ? "Fastify" :
|
|
643
|
+
null,
|
|
644
|
+
|
|
645
|
+
language: deps.typescript ? "TypeScript" : "JavaScript",
|
|
646
|
+
|
|
647
|
+
bundler: deps.vite ? "Vite" :
|
|
648
|
+
deps.webpack ? "Webpack" :
|
|
649
|
+
null,
|
|
650
|
+
|
|
651
|
+
styling: deps.tailwindcss ? "TailwindCSS" :
|
|
652
|
+
deps.sass ? "Sass" :
|
|
653
|
+
null,
|
|
654
|
+
|
|
655
|
+
database: deps.mongoose ? "MongoDB" :
|
|
656
|
+
deps.prisma ? "Prisma" :
|
|
657
|
+
deps.pg ? "PostgreSQL" :
|
|
658
|
+
null,
|
|
659
|
+
|
|
660
|
+
testing: deps.jest ? "Jest" :
|
|
661
|
+
deps.vitest ? "Vitest" :
|
|
662
|
+
null,
|
|
663
|
+
|
|
664
|
+
linting: deps.eslint ? "ESLint" : null,
|
|
665
|
+
|
|
666
|
+
formatting: deps.prettier ? "Prettier" : null,
|
|
667
|
+
|
|
668
|
+
node: detectarNodeVersion(projectRoot, packageJson)
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
if (analysis.frontend && analysis.backend) {
|
|
672
|
+
analysis.architecture = "Fullstack"
|
|
673
|
+
} else if (analysis.frontend) {
|
|
674
|
+
analysis.architecture = "Frontend"
|
|
675
|
+
} else if (analysis.backend) {
|
|
676
|
+
analysis.architecture = "Backend"
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
return analysis
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
function detectarPackageManager(projectRoot) {
|
|
683
|
+
if (fs.existsSync(path.join(projectRoot, "pnpm-lock.yaml"))) {
|
|
684
|
+
return "pnpm"
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
if (fs.existsSync(path.join(projectRoot, "yarn.lock"))) {
|
|
688
|
+
return "yarn"
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
if (fs.existsSync(path.join(projectRoot, "package-lock.json"))) {
|
|
692
|
+
return "npm"
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
return "npm"
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
function analisarScripts(scripts = {}, packageManager) {
|
|
699
|
+
const commands = {
|
|
700
|
+
install: packageManager === "npm"
|
|
701
|
+
? "npm install"
|
|
702
|
+
: `${packageManager} install`
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
if (scripts.dev) {
|
|
706
|
+
commands.dev = `${packageManager} run dev`
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
if (scripts.build) {
|
|
710
|
+
commands.build = `${packageManager} run build`
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
if (scripts.start) {
|
|
714
|
+
commands.start = packageManager === "npm"
|
|
715
|
+
? "npm start"
|
|
716
|
+
: `${packageManager} start`
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
if (scripts.preview) {
|
|
720
|
+
commands.preview = `${packageManager} run preview`
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
return commands
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
function diagnosticarExecucao(commands) {
|
|
727
|
+
const warnings = []
|
|
728
|
+
|
|
729
|
+
if (!commands.dev && !commands.start) {
|
|
730
|
+
warnings.push("No development or start script found.")
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
if (commands.build && !commands.start) {
|
|
734
|
+
warnings.push("Build script found but no start script for production.")
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
return warnings
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
function detectarNodeVersion(projectRoot, packageJson) {
|
|
741
|
+
if (packageJson.engines && packageJson.engines.node) {
|
|
742
|
+
return packageJson.engines.node
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
const nvmrcPath = path.join(projectRoot, ".nvmrc")
|
|
746
|
+
if (fs.existsSync(nvmrcPath)) {
|
|
747
|
+
return fs.readFileSync(nvmrcPath, "utf-8").trim()
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
const nodeVersionPath = path.join(projectRoot, ".node-version")
|
|
751
|
+
if (fs.existsSync(nodeVersionPath)) {
|
|
752
|
+
return fs.readFileSync(nodeVersionPath, "utf-8").trim()
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
return process.version
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
function detectarTechStack(deps = {}) {
|
|
759
|
+
const stack = new Set()
|
|
760
|
+
|
|
761
|
+
for (const dep in deps) {
|
|
762
|
+
if (TECH_MAP[dep]) {
|
|
763
|
+
stack.add(TECH_MAP[dep])
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
return Array.from(stack).sort()
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
function gerarTechStackSection(stack) {
|
|
771
|
+
if (!stack.length) return ""
|
|
772
|
+
|
|
773
|
+
let section = "## π Tech Stack\n\n"
|
|
774
|
+
|
|
775
|
+
stack.forEach(tech => {
|
|
776
|
+
section += `- ${tech}\n`
|
|
777
|
+
})
|
|
778
|
+
|
|
779
|
+
return section + "\n"
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
|
|
783
|
+
function formatarAnalise(analysis) {
|
|
784
|
+
console.log(`
|
|
785
|
+
Project Analysis
|
|
786
|
+
ββββββββββββββββββββββββ
|
|
787
|
+
`)
|
|
788
|
+
|
|
789
|
+
const entries = Object.entries(analysis)
|
|
790
|
+
|
|
791
|
+
for (const [key, value] of entries) {
|
|
792
|
+
if (value) {
|
|
793
|
+
const label = key.charAt(0).toUpperCase() + key.slice(1)
|
|
794
|
+
console.log(`β ${label}: ${value}`)
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
console.log("")
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
function formatarExecucao(commands, warnings) {
|
|
802
|
+
console.log(`Project Execution Strategy
|
|
803
|
+
ββββββββββββββββββββββββ`)
|
|
804
|
+
|
|
805
|
+
console.log(`Install:`)
|
|
806
|
+
console.log(` ${commands.install}\n`)
|
|
807
|
+
|
|
808
|
+
if (commands.dev) {
|
|
809
|
+
console.log(`Development:`)
|
|
810
|
+
console.log(` ${commands.dev}\n`)
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
if (commands.build && commands.start) {
|
|
814
|
+
console.log(`Production:`)
|
|
815
|
+
console.log(` ${commands.build}`)
|
|
816
|
+
console.log(` ${commands.start}\n`)
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
if (commands.preview) {
|
|
820
|
+
console.log(`Preview:`)
|
|
821
|
+
console.log(` ${commands.preview}\n`)
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
if (warnings.length > 0) {
|
|
825
|
+
console.log(`Warnings:`)
|
|
826
|
+
warnings.forEach(w => console.log(` β ${w}`))
|
|
827
|
+
console.log()
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
|
|
832
|
+
|
|
252
833
|
async function main() {
|
|
253
834
|
const config = loadConfig(projectRoot)
|
|
254
835
|
|
|
@@ -288,6 +869,7 @@ mdSmith β Available Commands
|
|
|
288
869
|
--tree Show project structure
|
|
289
870
|
--deps List dependencies
|
|
290
871
|
--readme Generate README
|
|
872
|
+
--analyze Project analysis
|
|
291
873
|
`)
|
|
292
874
|
process.exit(0)
|
|
293
875
|
} else if (args.includes("--tree")){
|
|
@@ -297,16 +879,45 @@ Project Structure
|
|
|
297
879
|
`);
|
|
298
880
|
console.log(scanDir(projectRoot, 0, config))
|
|
299
881
|
process.exit(0)
|
|
882
|
+
} else if (args.includes("--analyze")){
|
|
883
|
+
const analysis = analisarProjeto(packageJson, projectRoot)
|
|
884
|
+
const packageManager = detectarPackageManager(projectRoot)
|
|
885
|
+
|
|
886
|
+
formatarAnalise(analysis)
|
|
887
|
+
|
|
888
|
+
const commands = analisarScripts(packageJson.scripts, packageManager)
|
|
889
|
+
const warnings = diagnosticarExecucao(commands)
|
|
890
|
+
|
|
891
|
+
formatarExecucao(commands, warnings)
|
|
892
|
+
|
|
893
|
+
const issues = healthChecks(packageJson, projectRoot, analysis)
|
|
894
|
+
|
|
895
|
+
if (issues.length > 0) {
|
|
896
|
+
console.log("Health Checks")
|
|
897
|
+
console.log("ββββββββββββββββββββββββ")
|
|
898
|
+
issues.forEach(issue => {
|
|
899
|
+
let icon = "β "
|
|
900
|
+
|
|
901
|
+
if (issue.type === "critical") icon = "β"
|
|
902
|
+
if (issue.type === "info") icon = "βΉ"
|
|
903
|
+
|
|
904
|
+
console.log(`${icon} ${issue.message}`)
|
|
905
|
+
})
|
|
906
|
+
console.log()
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
process.exit(0)
|
|
300
910
|
} else if (args.includes("--deps")){
|
|
301
911
|
console.log(`
|
|
302
912
|
Dependencies
|
|
303
913
|
ββββββββββββββββββββββββ
|
|
304
914
|
`)
|
|
305
915
|
const deps = formatDependencies(packageJson.dependencies)
|
|
306
|
-
console.log(deps)
|
|
916
|
+
console.log(`${deps}\n`)
|
|
917
|
+
console.log()
|
|
307
918
|
process.exit(0)
|
|
308
919
|
} else if (args.includes("--readme")){
|
|
309
|
-
const content = await generateReadme(packageJson, scanDir(projectRoot, 0, config))
|
|
920
|
+
const content = await generateReadme(packageJson, scanDir(projectRoot, 0, config), config)
|
|
310
921
|
|
|
311
922
|
console.log(`
|
|
312
923
|
README Preview
|
|
@@ -315,28 +926,22 @@ README Preview
|
|
|
315
926
|
|
|
316
927
|
console.log(content)
|
|
317
928
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
if (confirm === "y" || confirm === "n") {
|
|
326
|
-
respostaValida = true
|
|
327
|
-
} else {
|
|
328
|
-
console.log("\nPlease type y or n.")
|
|
329
|
-
}
|
|
330
|
-
}
|
|
929
|
+
const confirmResponse = await prompts({
|
|
930
|
+
type: 'confirm',
|
|
931
|
+
name: 'generate',
|
|
932
|
+
message: 'Generate README-MDSMITH.md?',
|
|
933
|
+
initial: true
|
|
934
|
+
});
|
|
331
935
|
|
|
332
|
-
if (
|
|
333
|
-
fs.writeFileSync("README-MDSMITH.md", content)
|
|
334
|
-
console.log("\nREADME-MDSMITH.md created.\n")
|
|
335
|
-
process.exit(0)
|
|
936
|
+
if (confirmResponse.generate) {
|
|
937
|
+
fs.writeFileSync("README-MDSMITH.md", content);
|
|
938
|
+
console.log("\nREADME-MDSMITH.md created.\n");
|
|
336
939
|
} else {
|
|
337
|
-
console.log("\nOperation cancelled.\n")
|
|
338
|
-
process.exit(0)
|
|
940
|
+
console.log("\nOperation cancelled.\n");
|
|
339
941
|
}
|
|
942
|
+
|
|
943
|
+
process.exit(0);
|
|
944
|
+
|
|
340
945
|
} else {
|
|
341
946
|
console.log(`
|
|
342
947
|
Unknown command.
|
package/mdsmith.config.json
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "mdsmith",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.2.0",
|
|
5
5
|
"description": "CLI para gerar READMEs e arquivos Markdown",
|
|
6
6
|
"bin": {
|
|
7
7
|
"mdsmith": "./bin/index.js"
|
|
@@ -10,5 +10,8 @@
|
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"engines": {
|
|
12
12
|
"node": ">=18"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"prompts": "^2.4.2"
|
|
13
16
|
}
|
|
14
17
|
}
|