forgefy 1.0.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/bin/cli.js +425 -0
- package/package.json +22 -0
- package/templates/next/base/components/Containers/Body/index.tsx +8 -0
- package/templates/next/base/components/UI/Button/index.tsx +25 -0
- package/templates/next/base/components/UI/Button/styles.scss +34 -0
- package/templates/next/base/components/UI/Header/index.tsx +37 -0
- package/templates/next/base/components/UI/Header/styles.scss +28 -0
- package/templates/next/base/public/icons/next._icon.svg +1 -0
- package/templates/next/base/scss/_colors.scss +59 -0
- package/templates/next/base/scss/_mixins.scss +141 -0
- package/templates/next/base/scss/_typography.scss +34 -0
- package/templates/next/base/scss/_utilities.scss +145 -0
- package/templates/next/base_landpage/app/globals.scss +126 -0
- package/templates/next/base_landpage/app/layout.tsx +24 -0
- package/templates/next/base_landpage/app/page.tsx +11 -0
- package/templates/next-env.d.ts +2 -0
- package/templates/package-lock.json +1034 -0
- package/templates/package.json +14 -0
- package/templates/tsconfig.base.json +16 -0
- package/templates/tsconfig.json +7 -0
- package/templates/vue/base/.gitkeep +0 -0
- package/templates/vue/base/readme.md +0 -0
- package/templates/vue/base_landpage/index.css +0 -0
- package/templates/vue/base_landpage/index.html +0 -0
- package/templates/vue/base_sistema_robusto/index.html +0 -0
- package/templates/vue/base_sistema_robusto/index.js +0 -0
- package/templates/vue/base_sistema_simples/index.html +0 -0
package/bin/cli.js
ADDED
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { execSync } from 'child_process'
|
|
4
|
+
import fs from 'fs-extra'
|
|
5
|
+
import path from 'path'
|
|
6
|
+
import ora from 'ora'
|
|
7
|
+
import chalk from 'chalk'
|
|
8
|
+
import prompts from 'prompts'
|
|
9
|
+
import { fileURLToPath } from 'url'
|
|
10
|
+
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
12
|
+
const __dirname = path.dirname(__filename)
|
|
13
|
+
|
|
14
|
+
const TEMPLATES_DIR = path.resolve(__dirname, '../templates')
|
|
15
|
+
|
|
16
|
+
const PROJECT_VARIANTS = [
|
|
17
|
+
{ id: 'base_landpage', title: 'Landpage' },
|
|
18
|
+
{ id: 'base_sistema_simples', title: 'Sistema simples' },
|
|
19
|
+
{ id: 'base_sistema_robusto', title: 'Sistema robusto' }
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
const VARIANT_CHOICES = PROJECT_VARIANTS.map((v) => ({
|
|
23
|
+
title: v.title,
|
|
24
|
+
value: v.id
|
|
25
|
+
}))
|
|
26
|
+
|
|
27
|
+
function getVariantLabel(variantId) {
|
|
28
|
+
const found = PROJECT_VARIANTS.find((v) => v.id === variantId)
|
|
29
|
+
return found ? found.title : variantId
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const LIBS_BY_STACK = {
|
|
33
|
+
next: {
|
|
34
|
+
base_landpage: {
|
|
35
|
+
deps: ['next-intl', 'sass', 'typescript'],
|
|
36
|
+
devDeps: ['@types/node', '@types/react', '@types/react-dom']
|
|
37
|
+
},
|
|
38
|
+
base_sistema_simples: {
|
|
39
|
+
deps: ['next-intl', 'sass', 'typescript'],
|
|
40
|
+
devDeps: ['@types/node', '@types/react', '@types/react-dom']
|
|
41
|
+
},
|
|
42
|
+
base_sistema_robusto: {
|
|
43
|
+
deps: [
|
|
44
|
+
'leaflet',
|
|
45
|
+
'moment',
|
|
46
|
+
'next-intl',
|
|
47
|
+
'react-toastify',
|
|
48
|
+
'sass',
|
|
49
|
+
'typescript'
|
|
50
|
+
],
|
|
51
|
+
devDeps: [
|
|
52
|
+
'@types/leaflet',
|
|
53
|
+
'@types/node',
|
|
54
|
+
'@types/react',
|
|
55
|
+
'@types/react-dom'
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
vue: {
|
|
60
|
+
base_landpage: {
|
|
61
|
+
deps: ['sass'],
|
|
62
|
+
devDeps: []
|
|
63
|
+
},
|
|
64
|
+
base_sistema_simples: {
|
|
65
|
+
deps: ['sass', 'vue-router'],
|
|
66
|
+
devDeps: []
|
|
67
|
+
},
|
|
68
|
+
base_sistema_robusto: {
|
|
69
|
+
deps: ['sass', 'vue-router', 'pinia'],
|
|
70
|
+
devDeps: []
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function applyBaseLayout(cwd, baseTemplate, spinner, label) {
|
|
76
|
+
if (!fs.existsSync(baseTemplate)) {
|
|
77
|
+
return
|
|
78
|
+
}
|
|
79
|
+
spinner.text = `Aplicando ${label} (public só da base, restante em src/)...`
|
|
80
|
+
const projectPublic = path.join(cwd, 'public')
|
|
81
|
+
if (fs.existsSync(projectPublic)) {
|
|
82
|
+
fs.removeSync(projectPublic)
|
|
83
|
+
}
|
|
84
|
+
const basePublic = path.join(baseTemplate, 'public')
|
|
85
|
+
if (fs.existsSync(basePublic)) {
|
|
86
|
+
fs.copySync(basePublic, projectPublic, { overwrite: true })
|
|
87
|
+
}
|
|
88
|
+
const srcDir = path.join(cwd, 'src')
|
|
89
|
+
fs.ensureDirSync(srcDir)
|
|
90
|
+
for (const ent of fs.readdirSync(baseTemplate, { withFileTypes: true })) {
|
|
91
|
+
if (ent.name === 'public') {
|
|
92
|
+
continue
|
|
93
|
+
}
|
|
94
|
+
fs.copySync(
|
|
95
|
+
path.join(baseTemplate, ent.name),
|
|
96
|
+
path.join(srcDir, ent.name),
|
|
97
|
+
{ overwrite: true }
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function rewriteNextBaseImportsInSrc(projectRoot, spinner) {
|
|
103
|
+
const srcRoot = path.join(projectRoot, 'src')
|
|
104
|
+
if (!fs.existsSync(srcRoot)) {
|
|
105
|
+
return
|
|
106
|
+
}
|
|
107
|
+
spinner.text = 'Ajustando imports (next/base → @/)...'
|
|
108
|
+
const exts = new Set([
|
|
109
|
+
'.ts',
|
|
110
|
+
'.tsx',
|
|
111
|
+
'.js',
|
|
112
|
+
'.jsx',
|
|
113
|
+
'.mjs',
|
|
114
|
+
'.cjs',
|
|
115
|
+
'.scss',
|
|
116
|
+
'.sass',
|
|
117
|
+
'.css'
|
|
118
|
+
])
|
|
119
|
+
const prefixRe = /(["'`])next\/base\//g
|
|
120
|
+
function walk(dir) {
|
|
121
|
+
for (const ent of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
122
|
+
if (ent.name === 'node_modules' || ent.name === '.next') {
|
|
123
|
+
continue
|
|
124
|
+
}
|
|
125
|
+
const full = path.join(dir, ent.name)
|
|
126
|
+
if (ent.isDirectory()) {
|
|
127
|
+
walk(full)
|
|
128
|
+
continue
|
|
129
|
+
}
|
|
130
|
+
if (!exts.has(path.extname(ent.name))) {
|
|
131
|
+
continue
|
|
132
|
+
}
|
|
133
|
+
let content = fs.readFileSync(full, 'utf8')
|
|
134
|
+
const nextContent = content.replace(prefixRe, '$1@/')
|
|
135
|
+
if (nextContent !== content) {
|
|
136
|
+
fs.writeFileSync(full, nextContent, 'utf8')
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
walk(srcRoot)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function applyNextTemplates(cwd, variant, stackTemplatesRoot, spinner) {
|
|
144
|
+
const baseTemplate = path.join(stackTemplatesRoot, 'base')
|
|
145
|
+
const variantTemplate = path.join(stackTemplatesRoot, variant)
|
|
146
|
+
applyBaseLayout(cwd, baseTemplate, spinner, 'base Next.js')
|
|
147
|
+
const appPath = path.join(cwd, 'src', 'app')
|
|
148
|
+
const templateAppPath = path.join(variantTemplate, 'app')
|
|
149
|
+
const hasVariantTemplate = fs.existsSync(variantTemplate)
|
|
150
|
+
const hasVariantApp = fs.existsSync(templateAppPath)
|
|
151
|
+
if (hasVariantApp) {
|
|
152
|
+
if (fs.existsSync(appPath)) {
|
|
153
|
+
fs.removeSync(appPath)
|
|
154
|
+
}
|
|
155
|
+
spinner.text = 'Aplicando estrutura do projeto...'
|
|
156
|
+
fs.copySync(templateAppPath, appPath, { overwrite: true })
|
|
157
|
+
}
|
|
158
|
+
if (hasVariantTemplate) {
|
|
159
|
+
const srcDir = path.join(cwd, 'src')
|
|
160
|
+
for (const ent of fs.readdirSync(variantTemplate, { withFileTypes: true })) {
|
|
161
|
+
if (ent.name === 'app' || ent.name === 'public') {
|
|
162
|
+
continue
|
|
163
|
+
}
|
|
164
|
+
fs.copySync(
|
|
165
|
+
path.join(variantTemplate, ent.name),
|
|
166
|
+
path.join(srcDir, ent.name),
|
|
167
|
+
{ overwrite: true }
|
|
168
|
+
)
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
rewriteNextBaseImportsInSrc(cwd, spinner)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function applyVueTemplates(cwd, variant, stackTemplatesRoot, spinner) {
|
|
175
|
+
const baseTemplate = path.join(stackTemplatesRoot, 'base')
|
|
176
|
+
const variantTemplate = path.join(stackTemplatesRoot, variant)
|
|
177
|
+
applyBaseLayout(cwd, baseTemplate, spinner, 'base Vue')
|
|
178
|
+
if (!fs.existsSync(variantTemplate)) {
|
|
179
|
+
return
|
|
180
|
+
}
|
|
181
|
+
const templateSrc = path.join(variantTemplate, 'src')
|
|
182
|
+
const targetSrc = path.join(cwd, 'src')
|
|
183
|
+
if (fs.existsSync(templateSrc)) {
|
|
184
|
+
spinner.text = 'Aplicando estrutura do projeto (src)...'
|
|
185
|
+
fs.copySync(templateSrc, targetSrc, { overwrite: true })
|
|
186
|
+
}
|
|
187
|
+
fs.copySync(variantTemplate, cwd, {
|
|
188
|
+
overwrite: true,
|
|
189
|
+
filter: (filePath) => {
|
|
190
|
+
const rel = path.relative(variantTemplate, filePath)
|
|
191
|
+
if (!rel) return true
|
|
192
|
+
const first = rel.split(path.sep)[0]
|
|
193
|
+
return first !== 'src' && first !== 'public'
|
|
194
|
+
}
|
|
195
|
+
})
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function patchNextTurbopackRoot(projectRoot) {
|
|
199
|
+
const candidates = ['next.config.ts', 'next.config.mjs', 'next.config.js']
|
|
200
|
+
for (const name of candidates) {
|
|
201
|
+
const filePath = path.join(projectRoot, name)
|
|
202
|
+
if (!fs.existsSync(filePath)) {
|
|
203
|
+
continue
|
|
204
|
+
}
|
|
205
|
+
let content = fs.readFileSync(filePath, 'utf8')
|
|
206
|
+
if (/turbopack\s*:\s*\{[\s\S]*?root\s*:/s.test(content)) {
|
|
207
|
+
return
|
|
208
|
+
}
|
|
209
|
+
if (name === 'next.config.ts') {
|
|
210
|
+
if (!/\bimport\s+path\s+from\s+['"]path['"]/.test(content)) {
|
|
211
|
+
content = content.replace(
|
|
212
|
+
/^(import\s+type\s+\{\s*NextConfig\s*\}\s+from\s+['"]next['"];)\s*/m,
|
|
213
|
+
'$1\nimport path from "path";\n\n'
|
|
214
|
+
)
|
|
215
|
+
}
|
|
216
|
+
content = content.replace(
|
|
217
|
+
/(const\s+nextConfig:\s*NextConfig\s*=\s*\{)/,
|
|
218
|
+
'$1\n turbopack: {\n root: path.join(__dirname),\n },'
|
|
219
|
+
)
|
|
220
|
+
fs.writeFileSync(filePath, content, 'utf8')
|
|
221
|
+
return
|
|
222
|
+
}
|
|
223
|
+
if (name === 'next.config.mjs') {
|
|
224
|
+
const prelude = []
|
|
225
|
+
if (!/\bimport\s+path\s+from\s+['"]path['"]/.test(content)) {
|
|
226
|
+
prelude.push('import path from "path";')
|
|
227
|
+
}
|
|
228
|
+
if (!/fileURLToPath/.test(content)) {
|
|
229
|
+
prelude.push('import { fileURLToPath } from "url";')
|
|
230
|
+
}
|
|
231
|
+
if (!/\bconst __dirname\b/.test(content)) {
|
|
232
|
+
prelude.push(
|
|
233
|
+
'const __dirname = path.dirname(fileURLToPath(import.meta.url));'
|
|
234
|
+
)
|
|
235
|
+
}
|
|
236
|
+
if (prelude.length) {
|
|
237
|
+
content = `${prelude.join('\n')}\n\n${content}`
|
|
238
|
+
}
|
|
239
|
+
content = content.replace(
|
|
240
|
+
/(const\s+nextConfig[^=]*=\s*\{)/,
|
|
241
|
+
'$1\n turbopack: {\n root: path.join(__dirname),\n },'
|
|
242
|
+
)
|
|
243
|
+
fs.writeFileSync(filePath, content, 'utf8')
|
|
244
|
+
return
|
|
245
|
+
}
|
|
246
|
+
if (name === 'next.config.js') {
|
|
247
|
+
if (!/require\(['"]path['"]\)/.test(content)) {
|
|
248
|
+
content = `const path = require('path');\n${content}`
|
|
249
|
+
}
|
|
250
|
+
content = content.replace(
|
|
251
|
+
/(module\.exports\s*=\s*\{)/,
|
|
252
|
+
'$1\n turbopack: {\n root: path.join(__dirname),\n },'
|
|
253
|
+
)
|
|
254
|
+
fs.writeFileSync(filePath, content, 'utf8')
|
|
255
|
+
return
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const STACKS = {
|
|
261
|
+
next: {
|
|
262
|
+
id: 'next',
|
|
263
|
+
promptLabel: 'Next.js',
|
|
264
|
+
bannerStack: 'Next.js',
|
|
265
|
+
createMessage: 'Criando projeto Next.js...',
|
|
266
|
+
createProject: (projectName) => {
|
|
267
|
+
execSync(
|
|
268
|
+
`npx create-next-app@latest ${projectName} --typescript --eslint --app --src-dir --import-alias "@/*"`,
|
|
269
|
+
{ stdio: 'inherit' }
|
|
270
|
+
)
|
|
271
|
+
},
|
|
272
|
+
applyTemplates: applyNextTemplates
|
|
273
|
+
},
|
|
274
|
+
vue: {
|
|
275
|
+
id: 'vue',
|
|
276
|
+
promptLabel: 'Vue (Vite + TypeScript)',
|
|
277
|
+
bannerStack: 'Vue 3 (Vite)',
|
|
278
|
+
createMessage: 'Criando projeto Vue (Vite)...',
|
|
279
|
+
createProject: (projectName) => {
|
|
280
|
+
execSync(
|
|
281
|
+
`npm create vite@latest ${projectName} -- --template vue-ts`,
|
|
282
|
+
{ stdio: 'inherit' }
|
|
283
|
+
)
|
|
284
|
+
},
|
|
285
|
+
applyTemplates: applyVueTemplates
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const STACK_CHOICES = Object.values(STACKS).map((s) => ({
|
|
290
|
+
title: s.promptLabel,
|
|
291
|
+
value: s.id
|
|
292
|
+
}))
|
|
293
|
+
|
|
294
|
+
function showBanner(projectName, stackId, variant) {
|
|
295
|
+
const stack = STACKS[stackId]
|
|
296
|
+
const stackName = stack ? stack.bannerStack : stackId
|
|
297
|
+
|
|
298
|
+
console.log(`
|
|
299
|
+
${chalk.cyan.bold(`
|
|
300
|
+
███████╗ ██████╗ ██████╗ ██████╗ ███████╗
|
|
301
|
+
██╔════╝██╔═══██╗██╔══██╗██╔════╝ ██╔════╝
|
|
302
|
+
█████╗ ██║ ██║██████╔╝██║ ███╗█████╗
|
|
303
|
+
██╔══╝ ██║ ██║██╔══██╗██║ ██║██╔══╝
|
|
304
|
+
██║ ╚██████╔╝██║ ██║╚██████╔╝███████
|
|
305
|
+
╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═════╝
|
|
306
|
+
███████╗██╗ ██╗
|
|
307
|
+
██╔════╝╚██╗ ██╔╝
|
|
308
|
+
█████╗ ╚████╔╝
|
|
309
|
+
██╔══╝ ╚██╔╝
|
|
310
|
+
██║ ██║
|
|
311
|
+
╚═╝ ╚═╝
|
|
312
|
+
`)}
|
|
313
|
+
${chalk.magenta.bold('⚡ FORGE FY CLI')}
|
|
314
|
+
${chalk.gray('────────────────────────────────────────')}
|
|
315
|
+
${chalk.yellow('📦 Projeto:')} ${projectName}
|
|
316
|
+
${chalk.yellow('⚡ Stack:')} ${stackName}
|
|
317
|
+
${chalk.yellow('🎯 Tipo de sistema:')} ${getVariantLabel(variant)}
|
|
318
|
+
${chalk.gray('────────────────────────────────────────')}
|
|
319
|
+
${chalk.green('✔ Projeto criado com sucesso!')}
|
|
320
|
+
${chalk.gray('👨💻 Desenvolvido por Caio Fortes')}
|
|
321
|
+
`)
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
async function main() {
|
|
325
|
+
const response = await prompts([
|
|
326
|
+
{
|
|
327
|
+
type: 'text',
|
|
328
|
+
name: 'projectName',
|
|
329
|
+
message: 'Nome do projeto:',
|
|
330
|
+
initial: 'my-app'
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
type: 'select',
|
|
334
|
+
name: 'stack',
|
|
335
|
+
message: 'Linguagem / framework:',
|
|
336
|
+
choices: STACK_CHOICES
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
type: 'select',
|
|
340
|
+
name: 'variant',
|
|
341
|
+
message: 'Tipo de sistema:',
|
|
342
|
+
choices: VARIANT_CHOICES
|
|
343
|
+
}
|
|
344
|
+
])
|
|
345
|
+
|
|
346
|
+
const { projectName, stack: stackId, variant } = response
|
|
347
|
+
|
|
348
|
+
if (!projectName || !stackId || !variant) {
|
|
349
|
+
console.log(chalk.red('❌ Operação cancelada'))
|
|
350
|
+
process.exit(1)
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const stack = STACKS[stackId]
|
|
354
|
+
if (!stack) {
|
|
355
|
+
console.log(chalk.red(`❌ Stack não suportada: ${stackId}`))
|
|
356
|
+
process.exit(1)
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const stackLibs = LIBS_BY_STACK[stackId]
|
|
360
|
+
if (!stackLibs || !stackLibs[variant]) {
|
|
361
|
+
console.log(
|
|
362
|
+
chalk.red(
|
|
363
|
+
`❌ Variante "${variant}" sem dependências definidas para stack "${stackId}". Ajuste LIBS_BY_STACK no CLI.`
|
|
364
|
+
)
|
|
365
|
+
)
|
|
366
|
+
process.exit(1)
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const targetDir = path.join(process.cwd(), projectName)
|
|
370
|
+
|
|
371
|
+
if (fs.existsSync(targetDir)) {
|
|
372
|
+
console.log(chalk.red('❌ Pasta já existe'))
|
|
373
|
+
process.exit(1)
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const spinner = ora(stack.createMessage).start()
|
|
377
|
+
|
|
378
|
+
try {
|
|
379
|
+
stack.createProject(projectName)
|
|
380
|
+
|
|
381
|
+
process.chdir(projectName)
|
|
382
|
+
|
|
383
|
+
if (stackId === 'next') {
|
|
384
|
+
patchNextTurbopackRoot(process.cwd())
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
spinner.text = 'Instalando dependências do scaffold...'
|
|
388
|
+
execSync('npm install', { stdio: 'inherit' })
|
|
389
|
+
|
|
390
|
+
const { deps, devDeps } = stackLibs[variant]
|
|
391
|
+
|
|
392
|
+
spinner.text = 'Instalando dependências extras...'
|
|
393
|
+
|
|
394
|
+
if (deps.length) {
|
|
395
|
+
execSync(`npm install ${deps.join(' ')}`, {
|
|
396
|
+
stdio: 'inherit'
|
|
397
|
+
})
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
if (devDeps.length) {
|
|
401
|
+
execSync(`npm install -D ${devDeps.join(' ')}`, {
|
|
402
|
+
stdio: 'inherit'
|
|
403
|
+
})
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
const stackTemplatesRoot = path.join(TEMPLATES_DIR, stackId)
|
|
407
|
+
stack.applyTemplates(process.cwd(), variant, stackTemplatesRoot, spinner)
|
|
408
|
+
|
|
409
|
+
spinner.succeed('Projeto criado com sucesso!')
|
|
410
|
+
|
|
411
|
+
showBanner(projectName, stackId, variant)
|
|
412
|
+
|
|
413
|
+
console.log('\n👉 Próximos passos:')
|
|
414
|
+
console.log(chalk.yellow(`cd ${projectName}`))
|
|
415
|
+
console.log(chalk.yellow('npm run dev'))
|
|
416
|
+
|
|
417
|
+
} catch (error) {
|
|
418
|
+
spinner.fail('Erro ao criar projeto')
|
|
419
|
+
console.error(error)
|
|
420
|
+
process.exit(1)
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
main()
|
|
425
|
+
|
package/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "forgefy",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "CLI para iniciar projetos rapidamente",
|
|
5
|
+
"main": "cli.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
8
|
+
},
|
|
9
|
+
"bin": {
|
|
10
|
+
"create-forgefy-app": "./bin/cli.js"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [],
|
|
13
|
+
"author": "",
|
|
14
|
+
"license": "ISC",
|
|
15
|
+
"type": "module",
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"chalk": "^4.1.2",
|
|
18
|
+
"fs-extra": "^11.3.4",
|
|
19
|
+
"ora": "^5.4.1",
|
|
20
|
+
"prompts": "^2.4.2"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import type { CSSProperties, ReactNode } from 'react';
|
|
4
|
+
import './styles.scss';
|
|
5
|
+
|
|
6
|
+
export interface ButtonProps {
|
|
7
|
+
onClick?: () => void;
|
|
8
|
+
id?: string;
|
|
9
|
+
sx?: CSSProperties;
|
|
10
|
+
children?: ReactNode;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default function Button({ onClick, children, id, sx }: ButtonProps) {
|
|
14
|
+
return (
|
|
15
|
+
<button
|
|
16
|
+
id={id}
|
|
17
|
+
type="button"
|
|
18
|
+
onClick={onClick}
|
|
19
|
+
style={sx}
|
|
20
|
+
className="base-button"
|
|
21
|
+
>
|
|
22
|
+
{children}
|
|
23
|
+
</button>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
@use '@/scss/_colors.scss' as *;
|
|
2
|
+
@use '@/scss/_mixins.scss' as *;
|
|
3
|
+
|
|
4
|
+
.base-button {
|
|
5
|
+
padding: 1rem;
|
|
6
|
+
border-radius: 2rem;
|
|
7
|
+
font-family: inter-semibold, 'Inter', sans-serif;
|
|
8
|
+
border: 4px solid $black-green;
|
|
9
|
+
display: flex;
|
|
10
|
+
align-items: center;
|
|
11
|
+
gap: 1rem;
|
|
12
|
+
justify-content: center;
|
|
13
|
+
transition: transform 0.3s ease;
|
|
14
|
+
@include set-color-black-green;
|
|
15
|
+
@include set-background-light-green;
|
|
16
|
+
|
|
17
|
+
@include respond-to(md) {
|
|
18
|
+
padding: 1rem;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@include respond-to(sm) {
|
|
22
|
+
padding: 0.7rem;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.base-button:hover {
|
|
27
|
+
animation: pulse 0.6s ease;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@keyframes pulse {
|
|
31
|
+
0% { transform: scale(1); }
|
|
32
|
+
50% { transform: scale(1.15); }
|
|
33
|
+
100% { transform: scale(1); }
|
|
34
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import Image from 'next/image';
|
|
4
|
+
import './styles.scss';
|
|
5
|
+
|
|
6
|
+
export default function Header() {
|
|
7
|
+
const links = [
|
|
8
|
+
{ label: 'Quem somos' },
|
|
9
|
+
{ label: 'Especialistas' },
|
|
10
|
+
{ label: 'Empresas' },
|
|
11
|
+
{ label: 'Startups' },
|
|
12
|
+
{ label: 'Blog' },
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<div id="headerContainer">
|
|
17
|
+
<div className="logo">
|
|
18
|
+
<Image
|
|
19
|
+
src="/icons/next._icon.svg"
|
|
20
|
+
alt="logo"
|
|
21
|
+
width={96}
|
|
22
|
+
height={80}
|
|
23
|
+
priority
|
|
24
|
+
/>
|
|
25
|
+
</div>
|
|
26
|
+
<div>
|
|
27
|
+
<ul className="lista-horizontal">
|
|
28
|
+
{links.map((link, index) => (
|
|
29
|
+
<li key={index}>
|
|
30
|
+
<a href="#">{link.label}</a>
|
|
31
|
+
</li>
|
|
32
|
+
))}
|
|
33
|
+
</ul>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
@use '@/scss/_mixins' as *;
|
|
2
|
+
|
|
3
|
+
#headerContainer {
|
|
4
|
+
gap: 4rem;
|
|
5
|
+
padding: 4rem 0rem;
|
|
6
|
+
@include center-flex;
|
|
7
|
+
|
|
8
|
+
@include respond-to(lg) {
|
|
9
|
+
gap: 1.5rem;
|
|
10
|
+
a { @include responsive-font(1vw, 1.5vw); }
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.logo {
|
|
15
|
+
width: 6rem;
|
|
16
|
+
height: 5rem;
|
|
17
|
+
z-index: 1;
|
|
18
|
+
|
|
19
|
+
@include respond-to(md) {
|
|
20
|
+
width: 4rem;
|
|
21
|
+
height: 3rem;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.lista-horizontal {
|
|
26
|
+
@include row-flex;
|
|
27
|
+
gap: 3rem;
|
|
28
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
@use '@/scss/_mixins';
|
|
2
|
+
|
|
3
|
+
$light-green: #8DE42B;
|
|
4
|
+
$black-green: #2C4511;
|
|
5
|
+
$black: rgb(0, 0, 0);
|
|
6
|
+
|
|
7
|
+
// Cores secundárias
|
|
8
|
+
$white: #FFFFFF;
|
|
9
|
+
$light-grey: #F8F8F8;
|
|
10
|
+
$grey: #B6B6B6;
|
|
11
|
+
$lighter-black: #1E1F268C;
|
|
12
|
+
$row: #7a77778c;
|
|
13
|
+
|
|
14
|
+
// Mixins
|
|
15
|
+
|
|
16
|
+
// Cores primárias
|
|
17
|
+
@mixin set-color-light-green { color: $light-green; }
|
|
18
|
+
@mixin set-background-light-green { background-color: $light-green; }
|
|
19
|
+
|
|
20
|
+
@mixin set-color-black-green { color: $black-green; }
|
|
21
|
+
@mixin set-background-black-green { background-color: $black-green; }
|
|
22
|
+
|
|
23
|
+
@mixin set-color-black { color: $black; }
|
|
24
|
+
@mixin set-background-black { background-color: $black; }
|
|
25
|
+
|
|
26
|
+
// Cores secundárias
|
|
27
|
+
@mixin set-color-white { color: $white; }
|
|
28
|
+
@mixin set-background-white { background-color: $white; }
|
|
29
|
+
|
|
30
|
+
@mixin set-color-light-grey { color: $light-grey; }
|
|
31
|
+
@mixin set-background-light-grey { background-color: $light-grey; }
|
|
32
|
+
|
|
33
|
+
@mixin set-color-grey { color: $grey; }
|
|
34
|
+
@mixin set-background-grey { background-color: $grey; }
|
|
35
|
+
|
|
36
|
+
@mixin set-color-lighter-black { color: $lighter-black; }
|
|
37
|
+
@mixin set-background-lighter-black { background-color: $lighter-black; }
|
|
38
|
+
|
|
39
|
+
// Classes utilitárias
|
|
40
|
+
.color-light-green { @include set-color-light-green; }
|
|
41
|
+
.background-light-green { @include set-background-light-green; }
|
|
42
|
+
|
|
43
|
+
.color-black-green { @include set-color-black-green; }
|
|
44
|
+
.background-black-green { @include set-background-black-green; }
|
|
45
|
+
|
|
46
|
+
.color-black { @include set-color-black; }
|
|
47
|
+
.background-black { @include set-background-black; }
|
|
48
|
+
|
|
49
|
+
.color-white { @include set-color-white; }
|
|
50
|
+
.background-white { @include set-background-white; }
|
|
51
|
+
|
|
52
|
+
.color-light-grey { @include set-color-light-grey; }
|
|
53
|
+
.background-light-grey { @include set-background-light-grey; }
|
|
54
|
+
|
|
55
|
+
.color-grey { @include set-color-grey; }
|
|
56
|
+
.background-grey { @include set-background-grey; }
|
|
57
|
+
|
|
58
|
+
.color-lighter-black { @include set-color-lighter-black; }
|
|
59
|
+
.background-lighter-black { @include set-background-lighter-black; }
|