mdsmith 1.2.1 β†’ 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/README-MDSMITH.md CHANGED
@@ -8,9 +8,6 @@ CLI para gerar READMEs e arquivos Markdown
8
8
  - **Gerenciador de Pacotes:** npm
9
9
  - **Tipo:** Node.js Project
10
10
 
11
- ## πŸ›  Tecnologias
12
-
13
-
14
11
  ## πŸ“ Estrutura do Projeto
15
12
  ```
16
13
  πŸ“‚ bin
@@ -41,26 +38,10 @@ CLI para gerar READMEs e arquivos Markdown
41
38
 
42
39
  ```
43
40
 
44
- ## πŸš€ Primeiros Passos
45
-
46
-
47
41
  ```bash
48
42
  npm install
49
43
  ```
50
44
 
51
-
52
-
53
- ## πŸ”‘ VariΓ‘veis de Ambiente
54
- Crie um arquivo `.env` na raiz do projeto com as seguintes variΓ‘veis:
55
-
56
-
57
- ## πŸ—„ ConfiguraΓ§Γ£o do Banco de Dados
58
- Execute as migrations do Prisma:
59
-
60
-
61
-
62
-
63
-
64
45
  ---
65
46
 
66
47
  Gerado por **mdSmith**
package/README.md ADDED
Binary file
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.2.1",
4
+ "version": "1.2.2",
5
5
  "description": "CLI para gerar READMEs e arquivos Markdown",
6
6
  "bin": {
7
7
  "mdsmith": "./bin/index.js"
package/bin/beta.js DELETED
@@ -1,117 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // npx mdsmith --help # ajuda
4
- // npx mdsmith --tree # mostra estrutura
5
- // npx mdsmith --deps # mostra dependΓͺncias
6
- // npx mdsmith --readme # gera README
7
-
8
- const fs = require("fs");
9
-
10
- const path = require("path");
11
-
12
- const projectRoot = process.cwd();
13
-
14
- if(!fs.existsSync(path.join(process.cwd(), "package.json"))){
15
- console.log("Package.json nΓ£o encontrado.\nVerifique se vocΓͺ estΓ‘ rodando o comando na raiz do projeto.")
16
- process.exit(1)
17
- }
18
-
19
- const packageJson = JSON.parse(fs.readFileSync(path.join(projectRoot, "package.json"), "utf-8"));
20
-
21
- const args = process.argv.slice(2)
22
-
23
- const IGNORE_DIRS = ["node_modules", ".git", ".vscode"];
24
-
25
- function formatDependencies(deps) {
26
- if (!deps || Object.keys(deps).length === 0) {
27
- return "Projeto sem dependΓͺncias.";
28
- }
29
-
30
- return Object.entries(deps)
31
- .map(([name, version]) => `- ${name} ${version}`)
32
- .join("\n");
33
- }
34
-
35
- function scanDir(dirPath, padding) {
36
- const entries = fs.readdirSync(dirPath, { withFileTypes: true });
37
-
38
- let treeContent = ""
39
-
40
-
41
- for (const entry of entries) {
42
-
43
- const fullPath = path.join(dirPath, entry.name);
44
-
45
- if (entry.isDirectory()) {
46
- if (IGNORE_DIRS.includes(entry.name)){
47
- treeContent += `${" ".repeat(padding*2)} πŸ“‚ ${entry.name}\n`
48
- } else {
49
- treeContent += `${" ".repeat(padding*2)} πŸ“‚ ${entry.name}\n`
50
- padding ++
51
- treeContent += scanDir(fullPath, padding)
52
- padding --
53
- }
54
- } else {
55
- treeContent += `${" ".repeat(padding*2)} πŸ“„ ${entry.name}\n`
56
- }
57
- }
58
-
59
- return treeContent
60
- }
61
-
62
- function generateReadme(packageJson, tree){
63
- const content = `
64
- # ${packageJson.name || "Nome nΓ£o informado."}
65
-
66
- ${packageJson.description || "Projeto Node.js analisado automaticamente pelo mdSmith."}
67
-
68
- ## πŸ”– InformaΓ§Γ΅es
69
- - **VersΓ£o:** ${packageJson.version || "VersΓ£o nΓ£o informada."}
70
- - **Gerenciador:** npm
71
- - **Tipo:** Backend Node.js
72
-
73
- ## πŸš€ DependΓͺncias
74
- ${formatDependencies(packageJson.dependencies)}
75
-
76
- ## πŸ“ Estrutura do Projeto
77
- \`\`\`
78
- ${tree}
79
- \`\`\`
80
-
81
- πŸ“„ *README gerado automaticamente pelo **mdSmith***
82
- `
83
-
84
- return content
85
-
86
- }
87
-
88
- if(args.length == 0){
89
- console.log(`
90
- Bem vindo ao mdSmith!
91
- Analisador de projetos Node.js
92
-
93
- Digite npx mdsmith --help se quiser ver os comandos disponΓ­veis
94
- `);
95
- process.exit(0);
96
- } else if(args.includes("--help")){
97
- console.log(`
98
- npx mdsmith --help Mostra os comandos possΓ­veis
99
- npx mdsmith --tree Mostra a estrutura do projeto
100
- npx mdsmith --deps Mostra as dependΓͺncias
101
- npx mdsmith --readme Gera um README automaticamente
102
- `)
103
- } else if (args.includes("--tree")){
104
- console.log("\n🌳 Estrutura do projeto:\n");
105
- console.log(scanDir(projectRoot, 0))
106
- } else if (args.includes("--deps")){
107
- console.log(`O projeto ${packageJson.name} na versΓ£o ${packageJson.version}, tem as seguintes dependencias:`)
108
- const deps = packageJson.dependencies || "Projeto sem depedencias"
109
- console.log(deps)
110
- } else if (args.includes("--readme")){
111
- const content = generateReadme(packageJson, scanDir(projectRoot, 0))
112
-
113
- fs.writeFileSync("README-MDSMITH.md", content)
114
- console.log("βœ… README-MDSMITH.md gerado com sucesso!")
115
- } else {
116
- console.log("❌ Comando não reconhecido. Use npx mdsmith --help para ver as opçáes.")
117
- }
@@ -1,997 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // npx mdsmith --help # show help
4
- // npx mdsmith --init # create config file
5
- // npx mdsmith --tree # show project structure
6
- // npx mdsmith --deps # list dependencies
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
-
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
- }
82
-
83
- const fs = require("fs");
84
-
85
- const path = require("path");
86
-
87
- const prompts = require('prompts');
88
-
89
- const projectRoot = process.cwd();
90
-
91
- if(!fs.existsSync(path.join(process.cwd(), "package.json"))){
92
- console.log(`
93
- Error: package.json not found.
94
- Please run mdsmith from the project root.
95
- `)
96
- process.exit(1)
97
- }
98
-
99
- let packageJson
100
-
101
- try {
102
- packageJson = JSON.parse(
103
- fs.readFileSync(path.join(projectRoot, "package.json"), "utf-8")
104
- )
105
- } catch {
106
- console.log("Invalid package.json file.")
107
- process.exit(1)
108
- }
109
-
110
- const args = process.argv.slice(2)
111
-
112
- const defaultConfig = {
113
- ignore: ["node_modules", ".git", ".vscode"],
114
- depth: Infinity,
115
- emojis: true
116
- }
117
-
118
- function healthChecks(packageJson, projectRoot, analysis) {
119
- const checks = []
120
-
121
- if (!analysis.testing) {
122
- checks.push({
123
- type: "warning",
124
- message: "No test runner configured."
125
- })
126
- }
127
-
128
- if (!analysis.linting) {
129
- checks.push({
130
- type: "info",
131
- message: "No ESLint detected."
132
- })
133
- }
134
-
135
- if (!analysis.formatting) {
136
- checks.push({
137
- type: "info",
138
- message: "No Prettier detected."
139
- })
140
- }
141
-
142
- if (!packageJson.engines?.node) {
143
- checks.push({
144
- type: "critical",
145
- message: "Node version not defined in package.json engines."
146
- })
147
- }
148
-
149
- if (!fs.existsSync(path.join(projectRoot, "Dockerfile"))) {
150
- checks.push({
151
- type: "info",
152
- message: "No Dockerfile found."
153
- })
154
- }
155
-
156
- return checks
157
- }
158
-
159
-
160
- function formatDependencies(deps) {
161
- if (!deps || Object.keys(deps).length === 0) {
162
- return "No dependencies found.\n";
163
- }
164
-
165
- return Object.entries(deps)
166
- .map(([name, version]) => `- ${name} ${version}`)
167
- .join("\n");
168
- }
169
-
170
- function scanDir(dirPath, padding, config) {
171
- let entries
172
- try {
173
- entries = fs.readdirSync(dirPath, { withFileTypes: true })
174
- .sort((a, b) => {
175
- if (a.isDirectory() && !b.isDirectory()) return -1
176
- if (!a.isDirectory() && b.isDirectory()) return 1
177
- return a.name.localeCompare(b.name)
178
- })
179
- } catch {
180
- return ""
181
- }
182
-
183
- let treeContent = ""
184
-
185
-
186
- for (const entry of entries) {
187
-
188
- const fullPath = path.join(dirPath, entry.name);
189
-
190
- if (entry.isDirectory()) {
191
- if (config.ignore.includes(entry.name)){
192
- continue
193
- } else {
194
- treeContent += `${" ".repeat(padding*2)} πŸ“‚ ${entry.name}\n`
195
- if (config.depth === null || padding < config.depth){
196
- padding ++
197
- treeContent += scanDir(fullPath, padding, config)
198
- padding --
199
- } else {
200
- treeContent += `${" ".repeat((padding + 1) * 2)} Β·Β·Β· \n`
201
- }
202
- }
203
- } else {
204
- if (config.ignore.includes(entry.name)){
205
- continue
206
- } else{
207
- treeContent += `${" ".repeat(padding*2)} πŸ“„ ${entry.name}\n`
208
- }
209
- }
210
- }
211
-
212
- return treeContent
213
- }
214
-
215
- async function buscarNome(nome) {
216
- if (!nome) {
217
- const confirm = await prompts({
218
- type: 'confirm',
219
- name: 'value',
220
- message: 'Project name not found in package.json. Provide one?',
221
- initial: true
222
- });
223
-
224
- if (!confirm.value) {
225
- console.log("\nProject name will be set as Unnamed Project.\n");
226
- return "Unnamed Project";
227
- }
228
-
229
- const response = await prompts({
230
- type: 'text',
231
- name: 'value',
232
- message: 'Enter the project name:'
233
- });
234
-
235
- console.log(`\nProject name set to "${response.value}".\n`);
236
- return response.value || "Unnamed Project";
237
- }
238
-
239
- return nome;
240
- }
241
-
242
- async function buscarDescricao(descricao) {
243
- if (!descricao) {
244
- const confirm = await prompts({
245
- type: 'confirm',
246
- name: 'value',
247
- message: 'Project description not found. Provide one?',
248
- initial: true
249
- });
250
-
251
- if (!confirm.value) {
252
- console.log("\nDescription placeholder added.\n");
253
- return "*Project description goes here*";
254
- }
255
-
256
- const response = await prompts({
257
- type: 'text',
258
- name: 'value',
259
- message: 'Enter the project description:'
260
- });
261
-
262
- console.log("\nDescription saved.\n");
263
- return response.value || "*Project description goes here*";
264
- }
265
-
266
- return descricao;
267
- }
268
-
269
- function detectarTipoProjeto(packageJson) {
270
- const deps = {
271
- ...packageJson.dependencies,
272
- ...packageJson.devDependencies
273
- }
274
-
275
- if (deps.react) return "Frontend React"
276
- if (deps.next) return "Next.js Application"
277
- if (deps.express) return "API REST (Express)"
278
- if (deps.typescript) return "Projeto TypeScript"
279
-
280
- return "Node.js Project"
281
- }
282
-
283
- function formatScripts(scripts) {
284
- if (!scripts || Object.keys(scripts).length === 0) {
285
- return "No scripts available.\n";
286
- }
287
-
288
- return Object.entries(scripts)
289
- .map(([name, script]) => `- ${name} -> ${script}`)
290
- .join("\n");
291
- }
292
-
293
- function detectarEnvVars(projectRoot) {
294
- const envPath = path.join(projectRoot, ".env")
295
-
296
- if (!fs.existsSync(envPath)) return []
297
-
298
- const content = fs.readFileSync(envPath, "utf-8")
299
-
300
- const vars = content
301
- .split("\n")
302
- .map(line => line.trim())
303
- .filter(line =>
304
- line &&
305
- !line.startsWith("#") &&
306
- line.includes("=")
307
- )
308
- .map(line => line.replace(/^export\s+/, ""))
309
- .map(line => line.split("=")[0].trim())
310
-
311
- return vars
312
- }
313
-
314
- function gerarEnvSection(vars) {
315
- if (!vars.length) return ""
316
-
317
- let section = "## πŸ”‘ Environment Variables\n\n"
318
- section += "Create a `.env` file in the project root with the following variables:\n\n"
319
-
320
- vars.forEach(v => {
321
- section += `- ${v}\n`
322
- })
323
-
324
- return section + "\n"
325
- }
326
-
327
- function detectarPorta(scanData) {
328
- const filesToScan = [...scanData.jsFiles, ...scanData.tsFiles]
329
-
330
- for (const filePath of filesToScan) {
331
- try {
332
- const content = fs.readFileSync(filePath, "utf-8")
333
-
334
- // πŸ”Ž Regex robusta: pega .listen(...) mesmo com quebra de linha
335
- const listenRegex = /\.listen\s*\(\s*([\s\S]*?)\)/g
336
-
337
- let match
338
-
339
- while ((match = listenRegex.exec(content)) !== null) {
340
- const rawArgument = match[1].split(",")[0].trim()
341
-
342
- // =============================
343
- // βœ… 1️⃣ NΓΊmero direto
344
- // =============================
345
- const directNumber = rawArgument.match(/^\d+$/)
346
- if (directNumber) {
347
- return directNumber[0]
348
- }
349
-
350
- // =============================
351
- // βœ… 2️⃣ Fallback inline
352
- // =============================
353
- const inlineFallback = rawArgument.match(/\|\|\s*(\d+)/)
354
- if (inlineFallback) {
355
- return inlineFallback[1]
356
- }
357
-
358
- // =============================
359
- // βœ… 3️⃣ process.env.PORT
360
- // =============================
361
- if (rawArgument.includes("process.env.PORT")) {
362
- return "process.env.PORT"
363
- }
364
-
365
- // =============================
366
- // βœ… 4️⃣ Resolver variΓ‘vel
367
- // =============================
368
- const variableName = rawArgument.replace(/[^a-zA-Z0-9_]/g, "")
369
-
370
- if (!variableName) continue
371
-
372
- const variableRegex = new RegExp(
373
- `(const|let|var)\\s+${variableName}\\s*=\\s*([\\s\\S]*?);`
374
- )
375
-
376
- const variableMatch = content.match(variableRegex)
377
-
378
- if (variableMatch) {
379
- const variableValue = variableMatch[2]
380
-
381
- const fallbackMatch = variableValue.match(/\|\|\s*(\d+)/)
382
- if (fallbackMatch) {
383
- return fallbackMatch[1]
384
- }
385
-
386
- const numberMatch = variableValue.match(/\d+/)
387
- if (numberMatch) {
388
- return numberMatch[0]
389
- }
390
- }
391
- }
392
-
393
- } catch (err) {
394
- continue
395
- }
396
- }
397
-
398
- return null
399
- }
400
-
401
- async function perguntarPortaManual() {
402
- const response = await prompts({
403
- type: "text",
404
- name: "port",
405
- message: "⚠️ Não consegui detectar a porta automaticamente.\nDigite a porta da API (ou deixe vazio para pular):",
406
- validate: value => {
407
- if (!value) return true
408
- return /^\d+$/.test(value) || "Digite apenas nΓΊmeros"
409
- }
410
- })
411
-
412
- return response.port || null
413
- }
414
-
415
- function scanProjectFiles(projectRoot) {
416
- const result = {
417
- allFiles: [],
418
- jsFiles: [],
419
- tsFiles: [],
420
- envFiles: [],
421
- hasDockerfile: false,
422
- hasPrisma: false,
423
- hasSrcFolder: false,
424
- }
425
-
426
- function walk(dir) {
427
- let files
428
- try {
429
- files = fs.readdirSync(dir)
430
- } catch {
431
- return
432
- }
433
-
434
- for (const file of files) {
435
- const fullPath = path.join(dir, file)
436
-
437
- // ignorar node_modules e .git
438
- if (
439
- fullPath.includes("node_modules") ||
440
- fullPath.includes(".git")
441
- ) continue
442
-
443
- let stat
444
- try {
445
- const stat = fs.lstatSync(fullPath)
446
-
447
- if (stat.isSymbolicLink()) continue
448
- } catch {
449
- continue
450
- }
451
-
452
- if (stat.isDirectory()) {
453
- if (file === "src") {
454
- result.hasSrcFolder = true
455
- }
456
- walk(fullPath)
457
- } else {
458
- result.allFiles.push(fullPath)
459
-
460
- if (file.endsWith(".js")) result.jsFiles.push(fullPath)
461
- if (file.endsWith(".ts")) result.tsFiles.push(fullPath)
462
- if (file.startsWith(".env")) result.envFiles.push(fullPath)
463
-
464
- if (file === "Dockerfile") result.hasDockerfile = true
465
- if (fullPath.includes("prisma")) result.hasPrisma = true
466
- }
467
- }
468
- }
469
-
470
- walk(projectRoot)
471
-
472
- return result
473
- }
474
-
475
- function gerarGettingStarted(packageManager = "npm", scripts = {}, hasPrisma) {
476
- const lines = []
477
-
478
- lines.push(
479
- packageManager === "npm"
480
- ? "npm install"
481
- : `${packageManager} install`
482
- )
483
-
484
- if (hasPrisma) {
485
- lines.push("npx prisma migrate dev")
486
- }
487
-
488
- if (scripts.dev) {
489
- lines.push(getRunCommand("dev", packageManager))
490
- } else if (scripts.start) {
491
- lines.push(getRunCommand("start", packageManager))
492
- }
493
-
494
- return `
495
- ## πŸš€ Getting Started
496
-
497
- \`\`\`bash
498
- ${lines.join("\n")}
499
- \`\`\`
500
-
501
- `
502
- }
503
-
504
-
505
- function getRunCommand(script, packageManager) {
506
- if (packageManager === "yarn") return `yarn ${script}`
507
- if (packageManager === "pnpm") return `pnpm ${script}`
508
- return `npm run ${script}`
509
- }
510
-
511
-
512
-
513
- async function generateReadme(packageJson, tree, config){
514
-
515
- const deps = {
516
- ...packageJson.dependencies,
517
- ...packageJson.devDependencies
518
- }
519
-
520
- const techStack = detectarTechStack(deps)
521
- const techSection = gerarTechStackSection(techStack)
522
-
523
-
524
-
525
-
526
- const envVars = detectarEnvVars(projectRoot)
527
- const envSection = gerarEnvSection(envVars)
528
-
529
- const nomeProjeto = await buscarNome(packageJson.name);
530
- const descricaoProjeto = await buscarDescricao(packageJson.description);
531
- const idioma = await escolherLingua()
532
- const t = i18n[idioma] || i18n.en
533
-
534
- const packageManager = detectarPackageManager(projectRoot)
535
-
536
- const scanData = scanProjectFiles(projectRoot)
537
- let port = detectarPorta(scanData)
538
-
539
- if (!port) {
540
- port = await perguntarPortaManual()
541
- }
542
-
543
- const commands = analisarScripts(packageJson.scripts, packageManager)
544
-
545
- const hasPrisma = detectarPrisma(projectRoot, packageJson)
546
- const dbSection = hasPrisma ? gerarDatabaseSection(packageManager) : ""
547
-
548
- const gettingStartedCommands = gerarGettingStarted(
549
- packageManager,
550
- packageJson.scripts,
551
- hasPrisma
552
- )
553
-
554
-
555
-
556
- const content = `
557
- # ${nomeProjeto}
558
-
559
- ${descricaoProjeto}
560
-
561
- ## ${config.emojis ? "πŸ”–" : ""} ${t.projectInfo}
562
- - **${t.version}:** ${packageJson.version || "Not specified"}
563
- - **${t.packageManager}:** ${packageManager}
564
- - **${t.type}:** ${detectarTipoProjeto(packageJson)}
565
-
566
- ${techSection}
567
-
568
- ## ${config.emojis ? "πŸ“" : ""} ${t.projectStructure}
569
- \`\`\`
570
- ${tree}
571
- \`\`\`
572
-
573
- ${gettingStartedCommands}
574
-
575
- ${envSection}
576
- ${dbSection}
577
-
578
- ${port ? `
579
- ## 🌐 Server
580
-
581
- After starting, the server will run at:
582
-
583
- http://localhost:${port}
584
-
585
- ` : ""}
586
-
587
-
588
- ---
589
-
590
- ${t.generatedBy} **mdSmith**
591
- `
592
-
593
- return content
594
-
595
- }
596
-
597
- function detectarPrisma(projectRoot, packageJson) {
598
- const deps = {
599
- ...packageJson.dependencies,
600
- ...packageJson.devDependencies
601
- }
602
-
603
- const hasPrismaDep = deps.prisma || deps["@prisma/client"]
604
- const hasPrismaFolder = fs.existsSync(path.join(projectRoot, "prisma"))
605
-
606
- return hasPrismaDep && hasPrismaFolder
607
- }
608
-
609
- function gerarDatabaseSection(packageManager) {
610
- let section = "## πŸ—„ Database Setup\n\n"
611
- section += "Run Prisma migrations:\n\n"
612
- section += "```bash\n"
613
-
614
- if (packageManager === "npm") {
615
- section += "npx prisma migrate dev\n"
616
- section += "npx prisma generate\n"
617
- } else {
618
- section += "npx prisma migrate dev\n"
619
- section += "npx prisma generate\n"
620
- }
621
-
622
- section += "```\n\n"
623
-
624
- return section
625
- }
626
-
627
-
628
-
629
- async function escolherLingua() {
630
- const response = await prompts({
631
- type: 'select',
632
- name: 'lang',
633
- message: 'Choose README language',
634
- choices: [
635
- { title: 'English', value: 'en' },
636
- { title: 'PortuguΓͺs', value: 'pt' },
637
- { title: 'EspaΓ±ol', value: 'es' }
638
- ],
639
- initial: 0
640
- });
641
-
642
- return response.lang || 'en';
643
- }
644
-
645
- function loadConfig(projectRoot) {
646
- const configPath = path.join(projectRoot, "mdsmith.config.json")
647
-
648
- if (!fs.existsSync(configPath)) {
649
- return defaultConfig
650
- }
651
-
652
- try {
653
- const fileContent = fs.readFileSync(configPath, "utf-8")
654
- const userConfig = JSON.parse(fileContent)
655
-
656
- return {
657
- ...defaultConfig,
658
- ...userConfig,
659
- ignore: [
660
- ...defaultConfig.ignore,
661
- ...(userConfig.ignore || [])
662
- ]
663
- }
664
-
665
-
666
- } catch (error) {
667
- console.log("Failed to read mdsmith.config.json. Using default configuration.")
668
- return defaultConfig
669
- }
670
- }
671
-
672
- function analisarProjeto(packageJson, projectRoot) {
673
- const deps = {
674
- ...packageJson.dependencies,
675
- ...packageJson.devDependencies
676
- }
677
-
678
- const analysis = {
679
- frontend: deps.react ? "React" :
680
- deps.next ? "Next.js" :
681
- null,
682
-
683
- backend: deps.express ? "Express" :
684
- deps.fastify ? "Fastify" :
685
- null,
686
-
687
- language: deps.typescript ? "TypeScript" : "JavaScript",
688
-
689
- bundler: deps.vite ? "Vite" :
690
- deps.webpack ? "Webpack" :
691
- null,
692
-
693
- styling: deps.tailwindcss ? "TailwindCSS" :
694
- deps.sass ? "Sass" :
695
- null,
696
-
697
- database: deps.mongoose ? "MongoDB" :
698
- deps.prisma ? "Prisma" :
699
- deps.pg ? "PostgreSQL" :
700
- null,
701
-
702
- testing: deps.jest ? "Jest" :
703
- deps.vitest ? "Vitest" :
704
- null,
705
-
706
- linting: deps.eslint ? "ESLint" : null,
707
-
708
- formatting: deps.prettier ? "Prettier" : null,
709
-
710
- node: detectarNodeVersion(projectRoot, packageJson)
711
- }
712
-
713
- if (analysis.frontend && analysis.backend) {
714
- analysis.architecture = "Fullstack"
715
- } else if (analysis.frontend) {
716
- analysis.architecture = "Frontend"
717
- } else if (analysis.backend) {
718
- analysis.architecture = "Backend"
719
- }
720
-
721
- return analysis
722
- }
723
-
724
- function detectarPackageManager(projectRoot) {
725
- if (fs.existsSync(path.join(projectRoot, "pnpm-lock.yaml"))) {
726
- return "pnpm"
727
- }
728
-
729
- if (fs.existsSync(path.join(projectRoot, "yarn.lock"))) {
730
- return "yarn"
731
- }
732
-
733
- if (fs.existsSync(path.join(projectRoot, "package-lock.json"))) {
734
- return "npm"
735
- }
736
-
737
- return "npm"
738
- }
739
-
740
- function analisarScripts(scripts = {}, packageManager) {
741
- const commands = {
742
- install: packageManager === "npm"
743
- ? "npm install"
744
- : `${packageManager} install`
745
- }
746
-
747
- if (scripts.dev) {
748
- commands.dev = `${packageManager} run dev`
749
- }
750
-
751
- if (scripts.build) {
752
- commands.build = `${packageManager} run build`
753
- }
754
-
755
- if (scripts.start) {
756
- commands.start = packageManager === "npm"
757
- ? "npm start"
758
- : `${packageManager} start`
759
- }
760
-
761
- if (scripts.preview) {
762
- commands.preview = `${packageManager} run preview`
763
- }
764
-
765
- return commands
766
- }
767
-
768
- function diagnosticarExecucao(commands) {
769
- const warnings = []
770
-
771
- if (!commands.dev && !commands.start) {
772
- warnings.push("No development or start script found.")
773
- }
774
-
775
- if (commands.build && !commands.start) {
776
- warnings.push("Build script found but no start script for production.")
777
- }
778
-
779
- return warnings
780
- }
781
-
782
- function detectarNodeVersion(projectRoot, packageJson) {
783
- if (packageJson.engines && packageJson.engines.node) {
784
- return packageJson.engines.node
785
- }
786
-
787
- const nvmrcPath = path.join(projectRoot, ".nvmrc")
788
- if (fs.existsSync(nvmrcPath)) {
789
- return fs.readFileSync(nvmrcPath, "utf-8").trim()
790
- }
791
-
792
- const nodeVersionPath = path.join(projectRoot, ".node-version")
793
- if (fs.existsSync(nodeVersionPath)) {
794
- return fs.readFileSync(nodeVersionPath, "utf-8").trim()
795
- }
796
-
797
- return process.version
798
- }
799
-
800
- function detectarTechStack(deps = {}) {
801
- const stack = new Set()
802
-
803
- for (const dep in deps) {
804
- if (TECH_MAP[dep]) {
805
- stack.add(TECH_MAP[dep])
806
- }
807
- }
808
-
809
- return Array.from(stack).sort()
810
- }
811
-
812
- function gerarTechStackSection(stack) {
813
- if (!stack.length) return ""
814
-
815
- let section = "## πŸ›  Tech Stack\n\n"
816
-
817
- stack.forEach(tech => {
818
- section += `- ${tech}\n`
819
- })
820
-
821
- return section + "\n"
822
- }
823
-
824
-
825
- function formatarAnalise(analysis) {
826
- console.log(`
827
- Project Analysis
828
- ────────────────────────
829
- `)
830
-
831
- const entries = Object.entries(analysis)
832
-
833
- for (const [key, value] of entries) {
834
- if (value) {
835
- const label = key.charAt(0).toUpperCase() + key.slice(1)
836
- console.log(`βœ“ ${label}: ${value}`)
837
- }
838
- }
839
-
840
- console.log("")
841
- }
842
-
843
- function formatarExecucao(commands, warnings) {
844
- console.log(`Project Execution Strategy
845
- ────────────────────────`)
846
-
847
- console.log(`Install:`)
848
- console.log(` ${commands.install}\n`)
849
-
850
- if (commands.dev) {
851
- console.log(`Development:`)
852
- console.log(` ${commands.dev}\n`)
853
- }
854
-
855
- if (commands.build && commands.start) {
856
- console.log(`Production:`)
857
- console.log(` ${commands.build}`)
858
- console.log(` ${commands.start}\n`)
859
- }
860
-
861
- if (commands.preview) {
862
- console.log(`Preview:`)
863
- console.log(` ${commands.preview}\n`)
864
- }
865
-
866
- if (warnings.length > 0) {
867
- console.log(`Warnings:`)
868
- warnings.forEach(w => console.log(` ⚠ ${w}`))
869
- console.log()
870
- }
871
- }
872
-
873
-
874
-
875
- async function main() {
876
- const config = loadConfig(projectRoot)
877
-
878
- if(args.length == 0){
879
- console.log(`
880
- mdSmith
881
- Node.js project analyzer
882
- ────────────────────────
883
-
884
- Run "mdsmith --help" to view available commands.
885
- `);
886
- process.exit(0);
887
- }else if (args.includes("--init")) {
888
-
889
- const configPath = path.join(projectRoot, "mdsmith.config.json")
890
-
891
- if (fs.existsSync(configPath)) {
892
- console.log("mdsmith.config.json already exists.")
893
- process.exit(0)
894
- }
895
-
896
- fs.writeFileSync(
897
- configPath,
898
- JSON.stringify(defaultConfig, null, 2)
899
- )
900
-
901
- console.log("Configuration file created.")
902
- process.exit(0)
903
-
904
- } else if(args.includes("--help")){
905
- console.log(`
906
- mdSmith β€” Available Commands
907
- ────────────────────────
908
-
909
- --help Show help
910
- --init Create config file
911
- --tree Show project structure
912
- --deps List dependencies
913
- --readme Generate README
914
- --analyze Project analysis
915
- `)
916
- process.exit(0)
917
- } else if (args.includes("--tree")){
918
- console.log(`
919
- Project Structure
920
- ────────────────────────
921
- `);
922
- console.log(scanDir(projectRoot, 0, config))
923
- process.exit(0)
924
- } else if (args.includes("--analyze")){
925
- const analysis = analisarProjeto(packageJson, projectRoot)
926
- const packageManager = detectarPackageManager(projectRoot)
927
-
928
- formatarAnalise(analysis)
929
-
930
- const commands = analisarScripts(packageJson.scripts, packageManager)
931
- const warnings = diagnosticarExecucao(commands)
932
-
933
- formatarExecucao(commands, warnings)
934
-
935
- const issues = healthChecks(packageJson, projectRoot, analysis)
936
-
937
- if (issues.length > 0) {
938
- console.log("Health Checks")
939
- console.log("────────────────────────")
940
- issues.forEach(issue => {
941
- let icon = "⚠"
942
-
943
- if (issue.type === "critical") icon = "❌"
944
- if (issue.type === "info") icon = "β„Ή"
945
-
946
- console.log(`${icon} ${issue.message}`)
947
- })
948
- console.log()
949
- }
950
-
951
- process.exit(0)
952
- } else if (args.includes("--deps")){
953
- console.log(`
954
- Dependencies
955
- ────────────────────────
956
- `)
957
- const deps = formatDependencies(packageJson.dependencies)
958
- console.log(`${deps}\n`)
959
- console.log()
960
- process.exit(0)
961
- } else if (args.includes("--readme")){
962
- const content = await generateReadme(packageJson, scanDir(projectRoot, 0, config), config)
963
-
964
- console.log(`
965
- README Preview
966
- ────────────────────────
967
- `)
968
-
969
- console.log(content)
970
-
971
- const confirmResponse = await prompts({
972
- type: 'confirm',
973
- name: 'generate',
974
- message: 'Generate README-MDSMITH.md?',
975
- initial: true
976
- });
977
-
978
- if (confirmResponse.generate) {
979
- fs.writeFileSync("README-MDSMITH.md", content);
980
- console.log("\nREADME-MDSMITH.md created.\n");
981
- } else {
982
- console.log("\nOperation cancelled.\n");
983
- }
984
-
985
- process.exit(0);
986
-
987
- } else {
988
- console.log(`
989
- Unknown command.
990
- Run "mdsmith --help" for usage.
991
- `)
992
- process.exit(0)
993
- }
994
- }
995
-
996
- main()
997
-