@nimbuslab/cli 0.16.4 → 0.16.5

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.
@@ -1,334 +0,0 @@
1
- import * as p from "@clack/prompts"
2
- import pc from "picocolors"
3
- import { execSync, spawnSync } from "child_process"
4
-
5
- const PACKAGE_NAME = "@nimbuslab/cli"
6
-
7
- type PackageManager = "bun" | "npm" | "unknown"
8
-
9
- // Verifica se existe instalacao no bun global
10
- function hasBunInstall(): boolean {
11
- try {
12
- const result = spawnSync("bun", ["pm", "ls", "-g"], {
13
- encoding: "utf-8",
14
- shell: true,
15
- timeout: 5000,
16
- })
17
- return !!(result.stdout && result.stdout.includes(PACKAGE_NAME))
18
- } catch {
19
- return false
20
- }
21
- }
22
-
23
- // Verifica se existe instalacao no npm global
24
- function hasNpmInstall(): boolean {
25
- try {
26
- const result = spawnSync("npm", ["ls", "-g", PACKAGE_NAME, "--json"], {
27
- encoding: "utf-8",
28
- shell: true,
29
- timeout: 5000,
30
- })
31
- if (result.stdout) {
32
- const data = JSON.parse(result.stdout)
33
- return !!data.dependencies?.[PACKAGE_NAME]
34
- }
35
- } catch {
36
- // ignore
37
- }
38
- return false
39
- }
40
-
41
- // Remove instalacao duplicada para evitar conflitos
42
- // Prioriza npm (fnm/nvm), remove bun se tiver npm
43
- function cleanupDuplicateInstalls(): { cleaned: boolean; message: string } {
44
- const hasNpm = hasNpmInstall()
45
- const hasBun = hasBunInstall()
46
-
47
- if (hasNpm && hasBun) {
48
- // Tem nos dois - remover do bun (prioriza npm)
49
- try {
50
- execSync("bun remove -g @nimbuslab/cli", { stdio: "pipe", encoding: "utf-8" })
51
- return {
52
- cleaned: true,
53
- message: "Instalacao duplicada removida do bun (usando npm)",
54
- }
55
- } catch {
56
- return {
57
- cleaned: false,
58
- message: "Falha ao remover instalacao duplicada do bun",
59
- }
60
- }
61
- }
62
-
63
- return { cleaned: false, message: "" }
64
- }
65
-
66
- // Detecta se está usando fnm (Fast Node Manager)
67
- function isUsingFnm(): boolean {
68
- const fnmDir = process.env.FNM_DIR || process.env.FNM_MULTISHELL_PATH
69
- const checkCmd = process.platform === "win32" ? "where" : "which"
70
- const whichNode = spawnSync(checkCmd, ["node"], { encoding: "utf-8", shell: true })
71
- return !!(fnmDir || (whichNode.stdout && whichNode.stdout.includes("fnm")))
72
- }
73
-
74
- async function getAvailableVersions(): Promise<string[]> {
75
- try {
76
- const res = await fetch(`https://registry.npmjs.org/${PACKAGE_NAME}`)
77
- if (!res.ok) return []
78
- const data = await res.json() as { versions?: Record<string, unknown> }
79
- return Object.keys(data.versions || {}).reverse()
80
- } catch {
81
- return []
82
- }
83
- }
84
-
85
- async function getLatestVersion(): Promise<string | null> {
86
- try {
87
- const res = await fetch(`https://registry.npmjs.org/${PACKAGE_NAME}/latest`)
88
- if (!res.ok) return null
89
- const data = await res.json() as { version?: string }
90
- return data.version || null
91
- } catch {
92
- return null
93
- }
94
- }
95
-
96
- // Detecta qual package manager tem o pacote instalado globalmente
97
- // Prioriza npm (mais comum com fnm/nvm)
98
- function detectPackageManager(): PackageManager {
99
- // Tentar npm primeiro (prioridade)
100
- try {
101
- const npmResult = spawnSync("npm", ["ls", "-g", PACKAGE_NAME, "--json"], {
102
- encoding: "utf-8",
103
- shell: true,
104
- timeout: 5000,
105
- })
106
- if (npmResult.stdout) {
107
- const data = JSON.parse(npmResult.stdout)
108
- if (data.dependencies?.[PACKAGE_NAME]) {
109
- return "npm"
110
- }
111
- }
112
- } catch {
113
- // ignore
114
- }
115
-
116
- // Fallback para bun
117
- try {
118
- const bunResult = spawnSync("bun", ["pm", "ls", "-g"], {
119
- encoding: "utf-8",
120
- shell: true,
121
- timeout: 5000,
122
- })
123
- if (bunResult.stdout && bunResult.stdout.includes(PACKAGE_NAME)) {
124
- return "bun"
125
- }
126
- } catch {
127
- // ignore
128
- }
129
-
130
- return "unknown"
131
- }
132
-
133
- function getCurrentVersion(): string {
134
- // Tentar npm primeiro (prioridade)
135
- try {
136
- const result = spawnSync("npm", ["ls", "-g", PACKAGE_NAME, "--json"], {
137
- encoding: "utf-8",
138
- shell: true,
139
- timeout: 5000,
140
- })
141
- if (result.stdout) {
142
- const data = JSON.parse(result.stdout)
143
- if (data.dependencies?.[PACKAGE_NAME]?.version) {
144
- return data.dependencies[PACKAGE_NAME].version
145
- }
146
- }
147
- } catch {
148
- // ignore
149
- }
150
-
151
- // Fallback para bun
152
- try {
153
- const bunResult = spawnSync("bun", ["pm", "ls", "-g"], {
154
- encoding: "utf-8",
155
- shell: true,
156
- timeout: 5000,
157
- })
158
- if (bunResult.stdout) {
159
- // Formato: "@nimbuslab/cli@0.14.0"
160
- const match = bunResult.stdout.match(new RegExp(`${PACKAGE_NAME.replace("/", "\\/")}@([\\d.]+)`))
161
- if (match?.[1]) {
162
- return match[1]
163
- }
164
- }
165
- } catch {
166
- // ignore
167
- }
168
-
169
- return "unknown"
170
- }
171
-
172
- export async function update(args: string[]) {
173
- // Parse flags
174
- const forceFlag = args.includes("--force") || args.includes("-f")
175
- const filteredArgs = args.filter(a => a !== "--force" && a !== "-f")
176
- const flag = filteredArgs[0]
177
-
178
- // nimbus update --list
179
- if (flag === "--list" || flag === "-l") {
180
- p.intro(pc.cyan("Versoes disponiveis"))
181
-
182
- const spinner = p.spinner()
183
- spinner.start("Buscando versoes...")
184
-
185
- const versions = await getAvailableVersions()
186
- spinner.stop("Versoes encontradas")
187
-
188
- if (versions.length === 0) {
189
- p.log.error("Nao foi possivel buscar as versoes")
190
- return
191
- }
192
-
193
- const current = getCurrentVersion()
194
- const pm = detectPackageManager()
195
-
196
- console.log()
197
- console.log(pc.bold("Ultimas 10 versoes:"))
198
- versions.slice(0, 10).forEach((v, i) => {
199
- const isCurrent = v === current
200
- const prefix = isCurrent ? pc.green("-> ") : " "
201
- const suffix = isCurrent ? pc.dim(" (instalada)") : ""
202
- const isLatest = i === 0 ? pc.yellow(" (latest)") : ""
203
- console.log(`${prefix}${v}${suffix}${isLatest}`)
204
- })
205
- console.log()
206
- console.log(pc.dim(`Total: ${versions.length} versoes`))
207
- console.log(pc.dim(`Package manager detectado: ${pm === "unknown" ? "nenhum" : pm}`))
208
- console.log(pc.dim(`Instalar versao especifica: nimbus update <versao>`))
209
- console.log(pc.dim(`Forcar reinstalacao: nimbus update --force`))
210
- return
211
- }
212
-
213
- // nimbus update [version]
214
- const targetVersion = flag || "latest"
215
- const isSpecificVersion = flag && flag !== "latest" && !flag.startsWith("-")
216
-
217
- p.intro(pc.cyan(`Atualizando ${PACKAGE_NAME}`))
218
-
219
- const spinner = p.spinner()
220
-
221
- // Limpar instalacoes duplicadas automaticamente
222
- spinner.start("Verificando instalacoes...")
223
- const cleanup = cleanupDuplicateInstalls()
224
- if (cleanup.cleaned) {
225
- spinner.stop(pc.yellow(cleanup.message))
226
- } else {
227
- spinner.stop("OK")
228
- }
229
-
230
- // Detectar package manager
231
- spinner.start("Detectando package manager...")
232
- const detectedPm = detectPackageManager()
233
- // Usar npm como padrao se nenhum detectado (compativel com fnm/nvm)
234
- const pm = detectedPm === "unknown" ? "npm" : detectedPm
235
- spinner.stop(`Package manager: ${pm}${detectedPm === "unknown" ? " (padrao)" : ""}`)
236
-
237
- // Verificar versao atual
238
- spinner.start("Verificando versao atual...")
239
- const currentVersion = getCurrentVersion()
240
- spinner.stop(`Versao atual: ${currentVersion === "unknown" ? "nao instalado" : currentVersion}`)
241
-
242
- // Verificar versao alvo
243
- let latestVersion: string | null = null
244
- if (!isSpecificVersion) {
245
- spinner.start("Verificando ultima versao no npm...")
246
- latestVersion = await getLatestVersion()
247
- spinner.stop(`Ultima versao: ${latestVersion || "desconhecida"}`)
248
-
249
- if (!forceFlag && latestVersion && latestVersion === currentVersion) {
250
- p.log.success("Voce ja esta na ultima versao!")
251
- console.log(pc.dim(" Use --force para reinstalar"))
252
- return
253
- }
254
- }
255
-
256
- // Confirmar
257
- const versionText = isSpecificVersion ? targetVersion : (latestVersion || "latest")
258
- const actionText = forceFlag ? "Reinstalar" : "Atualizar"
259
- const confirmUpdate = await p.confirm({
260
- message: `${actionText} para ${versionText} usando ${pm}?`,
261
- initialValue: true,
262
- })
263
-
264
- if (p.isCancel(confirmUpdate) || !confirmUpdate) {
265
- p.cancel("Atualizacao cancelada")
266
- return
267
- }
268
-
269
- // Executar update
270
- spinner.start("Atualizando...")
271
-
272
- try {
273
- const packageSpec = isSpecificVersion
274
- ? `${PACKAGE_NAME}@${targetVersion}`
275
- : latestVersion
276
- ? `${PACKAGE_NAME}@${latestVersion}`
277
- : PACKAGE_NAME
278
-
279
- // Usar o package manager detectado
280
- if (pm === "bun") {
281
- // Bun: remover primeiro se --force, depois instalar
282
- if (forceFlag) {
283
- try {
284
- execSync(`bun remove -g ${PACKAGE_NAME}`, { stdio: "pipe", encoding: "utf-8" })
285
- } catch {
286
- // ignore - pode nao estar instalado
287
- }
288
- }
289
- execSync(`bun add -g ${packageSpec}`, { stdio: "pipe", encoding: "utf-8" })
290
- } else {
291
- // npm: usar --force se solicitado
292
- const forceArg = forceFlag ? " --force" : ""
293
- execSync(`npm install -g ${packageSpec}${forceArg}`, { stdio: "pipe", encoding: "utf-8" })
294
- }
295
-
296
- spinner.stop("Atualizacao concluida!")
297
-
298
- // Verificar nova versao
299
- const newVersion = getCurrentVersion()
300
-
301
- if (currentVersion === newVersion && !forceFlag) {
302
- p.log.warn("Versao nao mudou. Tente com --force")
303
- } else {
304
- p.log.success(`${PACKAGE_NAME} atualizado: ${currentVersion} -> ${newVersion}`)
305
- }
306
-
307
- // Se usa fnm, avisar sobre hash
308
- if (isUsingFnm()) {
309
- console.log()
310
- console.log(pc.yellow(" fnm detectado - execute para aplicar:"))
311
- console.log(pc.cyan(" hash -r"))
312
- console.log(pc.dim(" Ou abra um novo terminal."))
313
- }
314
-
315
- p.outro(pc.green("Pronto!"))
316
- } catch (error) {
317
- spinner.stop("Erro na atualizacao")
318
-
319
- const err = error as Error & { stderr?: string }
320
- p.log.error("Falha ao atualizar")
321
-
322
- if (err.stderr) {
323
- console.log(pc.dim(err.stderr))
324
- }
325
-
326
- console.log()
327
- console.log(pc.yellow("Tente manualmente:"))
328
- if (pm === "bun") {
329
- console.log(pc.cyan(` bun add -g ${PACKAGE_NAME}${isSpecificVersion ? `@${targetVersion}` : ""}`))
330
- } else {
331
- console.log(pc.cyan(` npm install -g ${PACKAGE_NAME}${isSpecificVersion ? `@${targetVersion}` : ""}`))
332
- }
333
- }
334
- }
@@ -1,251 +0,0 @@
1
- import * as p from "@clack/prompts"
2
- import pc from "picocolors"
3
- import { analyze } from "./analyze"
4
-
5
- interface UpgradePlan {
6
- current: string
7
- target: string
8
- breakingChanges: string[]
9
- steps: string[]
10
- complexity: "low" | "medium" | "high"
11
- }
12
-
13
- const UPGRADE_PLANS: Record<string, (currentVersion: string) => UpgradePlan | null> = {
14
- next: (current) => {
15
- const major = parseInt(current.replace(/[^0-9]/g, "").slice(0, 2))
16
- if (major >= 16) return null
17
-
18
- return {
19
- current: current,
20
- target: "16.x",
21
- complexity: major < 15 ? "high" : "medium",
22
- breakingChanges: [
23
- "next/image: Mudancas na API de otimizacao",
24
- "Middleware: Novo formato de config",
25
- "next.config: Algumas opcoes depreciadas",
26
- "Turbopack: Agora e o bundler padrao",
27
- ],
28
- steps: [
29
- "Atualizar next para ^16.0.0",
30
- "Atualizar react para ^19.0.0",
31
- "Atualizar react-dom para ^19.0.0",
32
- "Revisar next.config.ts",
33
- "Testar build: bun run build",
34
- "Testar dev: bun dev",
35
- ],
36
- }
37
- },
38
-
39
- react: (current) => {
40
- if (current.startsWith("19") || current.startsWith("^19")) return null
41
-
42
- return {
43
- current: current,
44
- target: "19.x",
45
- complexity: "medium",
46
- breakingChanges: [
47
- "forwardRef: Nao mais necessario, ref e prop regular",
48
- "useContext: Pode ser substituido por use(Context)",
49
- "Suspense: Mudancas em fallback behavior",
50
- "Async components: Novo suporte nativo",
51
- ],
52
- steps: [
53
- "Atualizar react para ^19.0.0",
54
- "Atualizar react-dom para ^19.0.0",
55
- "Atualizar @types/react para ^19.0.0",
56
- "Remover forwardRef (usar ref como prop)",
57
- "Revisar Suspense boundaries",
58
- "Testar todos os componentes",
59
- ],
60
- }
61
- },
62
-
63
- tailwind: (current) => {
64
- if (current.startsWith("4") || current.startsWith("^4")) return null
65
-
66
- return {
67
- current: current,
68
- target: "4.x",
69
- complexity: "medium",
70
- breakingChanges: [
71
- "Config: Agora e CSS-first (nao mais tailwind.config.js)",
72
- "@apply: Sintaxe mudou",
73
- "Cores: Novo sistema de tokens",
74
- "Plugins: API diferente",
75
- ],
76
- steps: [
77
- "Atualizar tailwindcss para ^4.0.0",
78
- "Converter tailwind.config.js para CSS",
79
- "Atualizar globals.css com @import 'tailwindcss'",
80
- "Revisar @apply usages",
81
- "Atualizar plugins para v4",
82
- "Testar todas as paginas",
83
- ],
84
- }
85
- },
86
-
87
- bun: () => ({
88
- current: "pnpm/npm/yarn",
89
- target: "bun",
90
- complexity: "low",
91
- breakingChanges: [
92
- "Lockfile: Formato diferente (bun.lockb)",
93
- "Scripts: Alguns podem precisar ajuste",
94
- "Workspaces: Sintaxe levemente diferente",
95
- ],
96
- steps: [
97
- "Remover node_modules",
98
- "Remover pnpm-lock.yaml / package-lock.json / yarn.lock",
99
- "Executar: bun install",
100
- "Atualizar scripts no package.json (npx -> bunx)",
101
- "Atualizar CI/CD configs",
102
- "Testar: bun dev, bun build",
103
- ],
104
- }),
105
-
106
- drizzle: () => ({
107
- current: "prisma",
108
- target: "drizzle",
109
- complexity: "high",
110
- breakingChanges: [
111
- "Schema: Formato TypeScript (nao mais .prisma)",
112
- "Queries: API completamente diferente",
113
- "Migrations: Sistema diferente",
114
- "Relations: Declaracao diferente",
115
- ],
116
- steps: [
117
- "Instalar drizzle-orm e drizzle-kit",
118
- "Converter schema.prisma para drizzle/schema.ts",
119
- "Configurar drizzle.config.ts",
120
- "Gerar migrations: bunx drizzle-kit generate",
121
- "Atualizar todas as queries",
122
- "Atualizar auth config (se usar)",
123
- "Remover @prisma/client e prisma",
124
- "Testar todas as operacoes de banco",
125
- ],
126
- }),
127
- }
128
-
129
- export async function upgrade(args: string[]) {
130
- const showPlan = args.includes("--plan")
131
- const target = args.find(a => !a.startsWith("-"))
132
-
133
- console.log()
134
-
135
- if (showPlan || !target) {
136
- // Analyze first
137
- console.log(pc.cyan(" Analisando projeto para plano de upgrade..."))
138
- console.log()
139
-
140
- const analysis = await analyze([".", "--quiet"])
141
-
142
- console.log(pc.bold(" Upgrades Disponiveis:"))
143
- console.log()
144
-
145
- let hasUpgrades = false
146
-
147
- // Check Next.js
148
- if (analysis.frameworkVersion && analysis.framework === "nextjs") {
149
- const planFn = UPGRADE_PLANS["next"]
150
- if (planFn) {
151
- const plan = planFn(analysis.frameworkVersion)
152
- if (plan) {
153
- hasUpgrades = true
154
- printUpgradePlan("Next.js", plan)
155
- }
156
- }
157
- }
158
-
159
- // Check React
160
- if (analysis.dependencies["react"]) {
161
- const planFn = UPGRADE_PLANS["react"]
162
- if (planFn) {
163
- const plan = planFn(analysis.dependencies["react"])
164
- if (plan) {
165
- hasUpgrades = true
166
- printUpgradePlan("React", plan)
167
- }
168
- }
169
- }
170
-
171
- // Check Tailwind
172
- const tailwindDep = analysis.dependencies["tailwindcss"] || analysis.devDependencies["tailwindcss"]
173
- if (tailwindDep) {
174
- const planFn = UPGRADE_PLANS["tailwind"]
175
- if (planFn) {
176
- const plan = planFn(tailwindDep)
177
- if (plan) {
178
- hasUpgrades = true
179
- printUpgradePlan("Tailwind CSS", plan)
180
- }
181
- }
182
- }
183
-
184
- // Check package manager
185
- if (analysis.packageManager !== "bun") {
186
- const planFn = UPGRADE_PLANS["bun"]
187
- if (planFn) {
188
- const plan = planFn("")
189
- if (plan) {
190
- hasUpgrades = true
191
- printUpgradePlan("Package Manager", plan)
192
- }
193
- }
194
- }
195
-
196
- // Check Prisma -> Drizzle
197
- if (analysis.database === "prisma") {
198
- const planFn = UPGRADE_PLANS["drizzle"]
199
- if (planFn) {
200
- const plan = planFn("")
201
- if (plan) {
202
- hasUpgrades = true
203
- printUpgradePlan("Database", plan)
204
- }
205
- }
206
- }
207
-
208
- if (!hasUpgrades) {
209
- console.log(pc.green(" Projeto ja esta atualizado!"))
210
- }
211
-
212
- console.log()
213
- console.log(pc.dim(" Para executar um upgrade especifico:"))
214
- console.log(pc.dim(" nimbus upgrade next"))
215
- console.log(pc.dim(" nimbus upgrade tailwind"))
216
- console.log(pc.dim(" nimbus upgrade bun"))
217
- console.log()
218
-
219
- return
220
- }
221
-
222
- // Execute specific upgrade
223
- console.log(pc.yellow(` Upgrade ${target} ainda nao implementado.`))
224
- console.log(pc.dim(" Por enquanto, siga os passos do --plan manualmente."))
225
- console.log()
226
- }
227
-
228
- function printUpgradePlan(name: string, plan: UpgradePlan) {
229
- const complexityColor = {
230
- low: pc.green,
231
- medium: pc.yellow,
232
- high: pc.red,
233
- }
234
-
235
- console.log(` ${pc.bold(name)}`)
236
- console.log(` ${pc.dim("Atual:")} ${plan.current} ${pc.dim("->")} ${pc.cyan(plan.target)}`)
237
- console.log(` ${pc.dim("Complexidade:")} ${complexityColor[plan.complexity](plan.complexity)}`)
238
- console.log()
239
-
240
- console.log(` ${pc.dim("Breaking Changes:")}`)
241
- plan.breakingChanges.forEach(bc => {
242
- console.log(` ${pc.yellow("!")} ${bc}`)
243
- })
244
- console.log()
245
-
246
- console.log(` ${pc.dim("Passos:")}`)
247
- plan.steps.forEach((step, i) => {
248
- console.log(` ${pc.dim(`${i + 1}.`)} ${step}`)
249
- })
250
- console.log()
251
- }