mdsmith 1.2.0 → 1.2.1
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 +26 -2
- package/bin/index.js +29 -812
- package/bin/vers/303/243o2.js +997 -0
- package/package.json +1 -1
- package/src/analysis/index.js +149 -0
- package/src/config/index.js +43 -0
- package/src/detectors/index.js +203 -0
- package/src/formatters/index.js +53 -0
- package/src/generators/index.js +27 -0
- package/src/interactive/index.js +95 -0
- package/src/readme/index.js +286 -0
- package/src/scanner/index.js +113 -0
package/bin/index.js
CHANGED
|
@@ -8,82 +8,20 @@
|
|
|
8
8
|
// npx mdsmith --analyze # project analyze
|
|
9
9
|
|
|
10
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
11
|
|
|
83
12
|
const fs = require("fs");
|
|
84
13
|
|
|
85
14
|
const path = require("path");
|
|
86
15
|
|
|
16
|
+
const core = require("../src/generators")
|
|
17
|
+
const detectors = require("../src/detectors")
|
|
18
|
+
const scanner = require("../src/scanner")
|
|
19
|
+
const readme = require("../src/readme")
|
|
20
|
+
const configModule = require("../src/config")
|
|
21
|
+
const analysisModule = require("../src/analysis")
|
|
22
|
+
const formatters = require("../src/formatters")
|
|
23
|
+
|
|
24
|
+
|
|
87
25
|
const prompts = require('prompts');
|
|
88
26
|
|
|
89
27
|
const projectRoot = process.cwd();
|
|
@@ -96,742 +34,21 @@ Please run mdsmith from the project root.
|
|
|
96
34
|
process.exit(1)
|
|
97
35
|
}
|
|
98
36
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const args = process.argv.slice(2)
|
|
102
|
-
|
|
103
|
-
const defaultConfig = {
|
|
104
|
-
ignore: ["node_modules", ".git", ".vscode"],
|
|
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
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
function formatDependencies(deps) {
|
|
152
|
-
if (!deps || Object.keys(deps).length === 0) {
|
|
153
|
-
return "No dependencies found.\n";
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
return Object.entries(deps)
|
|
157
|
-
.map(([name, version]) => `- ${name} ${version}`)
|
|
158
|
-
.join("\n");
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
function scanDir(dirPath, padding, config) {
|
|
162
|
-
const entries = fs.readdirSync(dirPath, { withFileTypes: true })
|
|
163
|
-
.sort((a, b) => {
|
|
164
|
-
if (a.isDirectory() && !b.isDirectory()) return -1
|
|
165
|
-
if (!a.isDirectory() && b.isDirectory()) return 1
|
|
166
|
-
return a.name.localeCompare(b.name)
|
|
167
|
-
})
|
|
168
|
-
|
|
169
|
-
let treeContent = ""
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
for (const entry of entries) {
|
|
173
|
-
|
|
174
|
-
const fullPath = path.join(dirPath, entry.name);
|
|
175
|
-
|
|
176
|
-
if (entry.isDirectory()) {
|
|
177
|
-
if (config.ignore.includes(entry.name)){
|
|
178
|
-
continue
|
|
179
|
-
} else {
|
|
180
|
-
treeContent += `${" ".repeat(padding*2)} 📂 ${entry.name}\n`
|
|
181
|
-
if (config.depth === null || padding < config.depth){
|
|
182
|
-
padding ++
|
|
183
|
-
treeContent += scanDir(fullPath, padding, config)
|
|
184
|
-
padding --
|
|
185
|
-
} else {
|
|
186
|
-
treeContent += `${" ".repeat((padding + 1) * 2)} ··· \n`
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
} else {
|
|
190
|
-
if (config.ignore.includes(entry.name)){
|
|
191
|
-
continue
|
|
192
|
-
} else{
|
|
193
|
-
treeContent += `${" ".repeat(padding*2)} 📄 ${entry.name}\n`
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
return treeContent
|
|
199
|
-
}
|
|
200
|
-
|
|
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
|
-
}
|
|
214
|
-
|
|
215
|
-
const response = await prompts({
|
|
216
|
-
type: 'text',
|
|
217
|
-
name: 'value',
|
|
218
|
-
message: 'Enter the project name:'
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
console.log(`\nProject name set to "${response.value}".\n`);
|
|
222
|
-
return response.value || "Unnamed Project";
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
return nome;
|
|
226
|
-
}
|
|
227
|
-
|
|
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
|
-
}
|
|
241
|
-
|
|
242
|
-
const response = await prompts({
|
|
243
|
-
type: 'text',
|
|
244
|
-
name: 'value',
|
|
245
|
-
message: 'Enter the project description:'
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
console.log("\nDescription saved.\n");
|
|
249
|
-
return response.value || "*Project description goes here*";
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
return descricao;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
function detectarTipoProjeto(packageJson) {
|
|
256
|
-
const deps = {
|
|
257
|
-
...packageJson.dependencies,
|
|
258
|
-
...packageJson.devDependencies
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
if (deps.react) return "Frontend React"
|
|
262
|
-
if (deps.next) return "Next.js Application"
|
|
263
|
-
if (deps.express) return "API REST (Express)"
|
|
264
|
-
if (deps.typescript) return "Projeto TypeScript"
|
|
265
|
-
|
|
266
|
-
return "Node.js Project"
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
function formatScripts(scripts) {
|
|
270
|
-
if (!scripts || Object.keys(scripts).length === 0) {
|
|
271
|
-
return "No scripts available.\n";
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
return Object.entries(scripts)
|
|
275
|
-
.map(([name, script]) => `- ${name} -> ${script}`)
|
|
276
|
-
.join("\n");
|
|
277
|
-
}
|
|
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
|
-
|
|
37
|
+
let packageJson
|
|
486
38
|
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
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
|
|
39
|
+
try {
|
|
40
|
+
packageJson = JSON.parse(
|
|
41
|
+
fs.readFileSync(path.join(projectRoot, "package.json"), "utf-8")
|
|
510
42
|
)
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
const content = `
|
|
515
|
-
# ${nomeProjeto}
|
|
516
|
-
|
|
517
|
-
${descricaoProjeto}
|
|
518
|
-
|
|
519
|
-
## ${config.emojis ? "🔖" : ""} ${t.projectInfo}
|
|
520
|
-
- **${t.version}:** ${packageJson.version || "Not specified"}
|
|
521
|
-
- **${t.packageManager}:** ${packageManager}
|
|
522
|
-
- **${t.type}:** ${detectarTipoProjeto(packageJson)}
|
|
523
|
-
|
|
524
|
-
${techSection}
|
|
525
|
-
|
|
526
|
-
## ${config.emojis ? "📁" : ""} ${t.projectStructure}
|
|
527
|
-
\`\`\`
|
|
528
|
-
${tree}
|
|
529
|
-
\`\`\`
|
|
530
|
-
|
|
531
|
-
${gettingStartedCommands}
|
|
532
|
-
|
|
533
|
-
${envSection}
|
|
534
|
-
${dbSection}
|
|
535
|
-
|
|
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**
|
|
549
|
-
`
|
|
550
|
-
|
|
551
|
-
return content
|
|
552
|
-
|
|
553
|
-
}
|
|
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
|
-
|
|
603
|
-
function loadConfig(projectRoot) {
|
|
604
|
-
const configPath = path.join(projectRoot, "mdsmith.config.json")
|
|
605
|
-
|
|
606
|
-
if (!fs.existsSync(configPath)) {
|
|
607
|
-
return defaultConfig
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
try {
|
|
611
|
-
const fileContent = fs.readFileSync(configPath, "utf-8")
|
|
612
|
-
const userConfig = JSON.parse(fileContent)
|
|
613
|
-
|
|
614
|
-
return {
|
|
615
|
-
...defaultConfig,
|
|
616
|
-
...userConfig,
|
|
617
|
-
ignore: [
|
|
618
|
-
...defaultConfig.ignore,
|
|
619
|
-
...(userConfig.ignore || [])
|
|
620
|
-
]
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
} catch (error) {
|
|
625
|
-
console.log("Failed to read mdsmith.config.json. Using default configuration.")
|
|
626
|
-
return defaultConfig
|
|
627
|
-
}
|
|
628
|
-
}
|
|
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"
|
|
43
|
+
} catch {
|
|
44
|
+
console.log("Invalid package.json file.")
|
|
45
|
+
process.exit(1)
|
|
780
46
|
}
|
|
781
47
|
|
|
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
|
-
|
|
48
|
+
const args = process.argv.slice(2)
|
|
832
49
|
|
|
833
50
|
async function main() {
|
|
834
|
-
const config = loadConfig(projectRoot)
|
|
51
|
+
const config = configModule.loadConfig(projectRoot)
|
|
835
52
|
|
|
836
53
|
if(args.length == 0){
|
|
837
54
|
console.log(`
|
|
@@ -853,7 +70,7 @@ Run "mdsmith --help" to view available commands.
|
|
|
853
70
|
|
|
854
71
|
fs.writeFileSync(
|
|
855
72
|
configPath,
|
|
856
|
-
JSON.stringify(defaultConfig, null, 2)
|
|
73
|
+
JSON.stringify(configModule.defaultConfig, null, 2)
|
|
857
74
|
)
|
|
858
75
|
|
|
859
76
|
console.log("Configuration file created.")
|
|
@@ -877,20 +94,20 @@ mdSmith — Available Commands
|
|
|
877
94
|
Project Structure
|
|
878
95
|
────────────────────────
|
|
879
96
|
`);
|
|
880
|
-
console.log(scanDir(projectRoot, 0, config))
|
|
97
|
+
console.log(scanner.scanDir(projectRoot, 0, config))
|
|
881
98
|
process.exit(0)
|
|
882
99
|
} else if (args.includes("--analyze")){
|
|
883
|
-
const analysis = analisarProjeto(packageJson, projectRoot)
|
|
884
|
-
const packageManager = detectarPackageManager(projectRoot)
|
|
100
|
+
const analysis = analysisModule.analisarProjeto(packageJson, projectRoot)
|
|
101
|
+
const packageManager = detectors.detectarPackageManager(projectRoot)
|
|
885
102
|
|
|
886
|
-
formatarAnalise(analysis)
|
|
103
|
+
formatters.formatarAnalise(analysis)
|
|
887
104
|
|
|
888
|
-
const commands = analisarScripts(packageJson.scripts, packageManager)
|
|
889
|
-
const warnings = diagnosticarExecucao(commands)
|
|
105
|
+
const commands = analysisModule.analisarScripts(packageJson.scripts, packageManager)
|
|
106
|
+
const warnings = analysisModule.diagnosticarExecucao(commands)
|
|
890
107
|
|
|
891
|
-
formatarExecucao(commands, warnings)
|
|
108
|
+
formatters.formatarExecucao(commands, warnings)
|
|
892
109
|
|
|
893
|
-
const issues = healthChecks(packageJson, projectRoot, analysis)
|
|
110
|
+
const issues = analysisModule.healthChecks(packageJson, projectRoot, analysis)
|
|
894
111
|
|
|
895
112
|
if (issues.length > 0) {
|
|
896
113
|
console.log("Health Checks")
|
|
@@ -912,12 +129,12 @@ Project Structure
|
|
|
912
129
|
Dependencies
|
|
913
130
|
────────────────────────
|
|
914
131
|
`)
|
|
915
|
-
const deps = formatDependencies(packageJson.dependencies)
|
|
132
|
+
const deps = core.formatDependencies(packageJson.dependencies)
|
|
916
133
|
console.log(`${deps}\n`)
|
|
917
134
|
console.log()
|
|
918
135
|
process.exit(0)
|
|
919
136
|
} else if (args.includes("--readme")){
|
|
920
|
-
const content = await generateReadme(packageJson, scanDir(projectRoot, 0, config), config)
|
|
137
|
+
const content = await readme.generateReadme(packageJson, scanner.scanDir(projectRoot, 0, config), config)
|
|
921
138
|
|
|
922
139
|
console.log(`
|
|
923
140
|
README Preview
|