create-nexora-next 0.3.7 → 0.4.7
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.md +45 -45
- package/package.json +46 -46
- package/src/index.js +241 -266
- package/src/steps/01-create-next-app.js +18 -18
- package/src/steps/02-shadcn.js +38 -38
- package/src/steps/03-base-files.js +109 -109
- package/src/steps/04-providers.js +51 -51
- package/src/steps/05-i18n.js +41 -41
- package/src/steps/06-auth.js +30 -30
- package/src/steps/07-proxy.js +16 -16
- package/src/steps/08-install-deps.js +82 -82
- package/src/steps/09-husky.js +58 -58
- package/src/steps/10-axios.js +14 -14
- package/src/steps/11-patch-pkg.js +21 -21
- package/src/templates/files.js +908 -908
- package/src/utils/args.js +119 -0
- package/src/utils/runner.js +49 -49
- package/src/utils/safe-step.js +42 -42
- package/src/utils/writer.js +37 -37
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
const VALID_PMS = ['npm', 'pnpm', 'bun', 'yarn']
|
|
2
|
+
|
|
3
|
+
const FLAGS = {
|
|
4
|
+
'--all': { key: 'all' },
|
|
5
|
+
'--i18n': { key: 'i18n' },
|
|
6
|
+
'--query': { key: 'query' },
|
|
7
|
+
'--theming': { key: 'theming' },
|
|
8
|
+
'--animations': { key: 'animations' },
|
|
9
|
+
'--auth': { key: 'auth' },
|
|
10
|
+
'--axios': { key: 'axios' },
|
|
11
|
+
'--husky': { key: 'husky' },
|
|
12
|
+
'--rc': { key: 'reactCompiler' },
|
|
13
|
+
'--react-compiler': { key: 'reactCompiler' },
|
|
14
|
+
'--no-theming': { key: 'theming', value: false },
|
|
15
|
+
'--no-husky': { key: 'husky', value: false },
|
|
16
|
+
'--no-rc': { key: 'reactCompiler', value: false },
|
|
17
|
+
'--pm': { key: 'pm', takesValue: true },
|
|
18
|
+
'-y': { key: 'yes' },
|
|
19
|
+
'--yes': { key: 'yes' },
|
|
20
|
+
'--help': { key: 'help' },
|
|
21
|
+
'-h': { key: 'help' },
|
|
22
|
+
'--version': { key: 'version' },
|
|
23
|
+
'-v': { key: 'version' },
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function parseArgs(argv) {
|
|
27
|
+
const args = argv.slice(2)
|
|
28
|
+
const result = {
|
|
29
|
+
name: null,
|
|
30
|
+
pm: null,
|
|
31
|
+
yes: false,
|
|
32
|
+
help: false,
|
|
33
|
+
version: false,
|
|
34
|
+
all: false,
|
|
35
|
+
i18n: null,
|
|
36
|
+
query: null,
|
|
37
|
+
theming: null,
|
|
38
|
+
animations: null,
|
|
39
|
+
auth: null,
|
|
40
|
+
axios: null,
|
|
41
|
+
husky: null,
|
|
42
|
+
reactCompiler: null,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let i = 0
|
|
46
|
+
while (i < args.length) {
|
|
47
|
+
const arg = args[i]
|
|
48
|
+
const def = FLAGS[arg]
|
|
49
|
+
|
|
50
|
+
if (def) {
|
|
51
|
+
if (def.takesValue) {
|
|
52
|
+
result[def.key] = args[++i] ?? null
|
|
53
|
+
} else if (def.value === false) {
|
|
54
|
+
result[def.key] = false
|
|
55
|
+
} else {
|
|
56
|
+
result[def.key] = true
|
|
57
|
+
}
|
|
58
|
+
} else if (!arg.startsWith('-') && !result.name) {
|
|
59
|
+
result.name = arg
|
|
60
|
+
}
|
|
61
|
+
i++
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (result.all) {
|
|
65
|
+
for (const key of ['i18n','query','theming','animations','auth','axios','husky','reactCompiler']) {
|
|
66
|
+
if (result[key] === null) result[key] = true
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (result.pm && !VALID_PMS.includes(result.pm)) {
|
|
71
|
+
result.pm = null
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return result
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function hasAnyFlag(parsed) {
|
|
78
|
+
return (
|
|
79
|
+
parsed.yes ||
|
|
80
|
+
parsed.all ||
|
|
81
|
+
parsed.name !== null ||
|
|
82
|
+
parsed.pm !== null ||
|
|
83
|
+
['i18n','query','theming','animations','auth','axios','husky','reactCompiler']
|
|
84
|
+
.some(k => parsed[k] !== null)
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function printHelp(version) {
|
|
89
|
+
console.log(`
|
|
90
|
+
create-nexora-next v${version}
|
|
91
|
+
|
|
92
|
+
Usage:
|
|
93
|
+
npx create-nexora-next [name] [flags]
|
|
94
|
+
|
|
95
|
+
Flags:
|
|
96
|
+
--pm <npm|pnpm|bun|yarn> Package manager (default: prompt)
|
|
97
|
+
--all Enable every feature
|
|
98
|
+
--i18n Localization via next-intl
|
|
99
|
+
--query TanStack Query + persistence
|
|
100
|
+
--theming Theming via next-themes
|
|
101
|
+
--animations Motion + GSAP
|
|
102
|
+
--auth Auth proxy + validators
|
|
103
|
+
--axios Axios client/server instances
|
|
104
|
+
--husky Husky pre-commit hooks
|
|
105
|
+
--rc / --react-compiler React Compiler
|
|
106
|
+
--no-theming Skip theming
|
|
107
|
+
--no-husky Skip Husky
|
|
108
|
+
--no-rc Skip React Compiler
|
|
109
|
+
-y / --yes Accept all defaults (no prompts)
|
|
110
|
+
-v / --version Print version
|
|
111
|
+
-h / --help Show this help
|
|
112
|
+
|
|
113
|
+
Examples:
|
|
114
|
+
npx create-nexora-next my-app --pm pnpm --all
|
|
115
|
+
npx create-nexora-next my-app --pm bun --i18n --query --theming
|
|
116
|
+
npx create-nexora-next my-app --pm bun --all --no-animations --no-auth
|
|
117
|
+
npx create-nexora-next my-app -y
|
|
118
|
+
`)
|
|
119
|
+
}
|
package/src/utils/runner.js
CHANGED
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
import { execSync } from 'child_process'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Run a shell command with live output, throwing on failure.
|
|
5
|
-
* @param {string} cmd
|
|
6
|
-
* @param {string} cwd
|
|
7
|
-
*/
|
|
8
|
-
export function run(cmd, cwd) {
|
|
9
|
-
execSync(cmd, { cwd, stdio: 'inherit' })
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Run silently, capturing output. Returns stdout string.
|
|
14
|
-
* @param {string} cmd
|
|
15
|
-
* @param {string} cwd
|
|
16
|
-
*/
|
|
17
|
-
export function runSilent(cmd, cwd) {
|
|
18
|
-
return execSync(cmd, { cwd, stdio: 'pipe' }).toString().trim()
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Returns the install command for the given package manager.
|
|
23
|
-
* @param {'npm'|'pnpm'|'bun'|'yarn'} pm
|
|
24
|
-
* @param {string[]} deps
|
|
25
|
-
* @param {boolean} dev
|
|
26
|
-
*/
|
|
27
|
-
export function installCmd(pm, deps, dev = false) {
|
|
28
|
-
const flag = dev
|
|
29
|
-
? pm === 'npm' ? '--save-dev' : '-D'
|
|
30
|
-
: ''
|
|
31
|
-
const pkgs = deps.join(' ')
|
|
32
|
-
switch (pm) {
|
|
33
|
-
case 'yarn': return `yarn add ${flag} ${pkgs}`.trim()
|
|
34
|
-
case 'pnpm': return `pnpm add ${flag} ${pkgs}`.trim()
|
|
35
|
-
case 'bun': return `bun add ${flag} ${pkgs}`.trim()
|
|
36
|
-
default: return `npm install ${flag} ${pkgs}`.trim()
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Returns the exec command (npx / pnpx / bunx).
|
|
42
|
-
* @param {'npm'|'pnpm'|'bun'|'yarn'} pm
|
|
43
|
-
*/
|
|
44
|
-
export function execCmd(pm) {
|
|
45
|
-
switch (pm) {
|
|
46
|
-
case 'pnpm': return 'pnpx'
|
|
47
|
-
case 'bun': return 'bunx'
|
|
48
|
-
default: return 'npx'
|
|
49
|
-
}
|
|
1
|
+
import { execSync } from 'child_process'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Run a shell command with live output, throwing on failure.
|
|
5
|
+
* @param {string} cmd
|
|
6
|
+
* @param {string} cwd
|
|
7
|
+
*/
|
|
8
|
+
export function run(cmd, cwd) {
|
|
9
|
+
execSync(cmd, { cwd, stdio: 'inherit' })
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Run silently, capturing output. Returns stdout string.
|
|
14
|
+
* @param {string} cmd
|
|
15
|
+
* @param {string} cwd
|
|
16
|
+
*/
|
|
17
|
+
export function runSilent(cmd, cwd) {
|
|
18
|
+
return execSync(cmd, { cwd, stdio: 'pipe' }).toString().trim()
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Returns the install command for the given package manager.
|
|
23
|
+
* @param {'npm'|'pnpm'|'bun'|'yarn'} pm
|
|
24
|
+
* @param {string[]} deps
|
|
25
|
+
* @param {boolean} dev
|
|
26
|
+
*/
|
|
27
|
+
export function installCmd(pm, deps, dev = false) {
|
|
28
|
+
const flag = dev
|
|
29
|
+
? pm === 'npm' ? '--save-dev' : '-D'
|
|
30
|
+
: ''
|
|
31
|
+
const pkgs = deps.join(' ')
|
|
32
|
+
switch (pm) {
|
|
33
|
+
case 'yarn': return `yarn add ${flag} ${pkgs}`.trim()
|
|
34
|
+
case 'pnpm': return `pnpm add ${flag} ${pkgs}`.trim()
|
|
35
|
+
case 'bun': return `bun add ${flag} ${pkgs}`.trim()
|
|
36
|
+
default: return `npm install ${flag} ${pkgs}`.trim()
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Returns the exec command (npx / pnpx / bunx).
|
|
42
|
+
* @param {'npm'|'pnpm'|'bun'|'yarn'} pm
|
|
43
|
+
*/
|
|
44
|
+
export function execCmd(pm) {
|
|
45
|
+
switch (pm) {
|
|
46
|
+
case 'pnpm': return 'pnpx'
|
|
47
|
+
case 'bun': return 'bunx'
|
|
48
|
+
default: return 'npx'
|
|
49
|
+
}
|
|
50
50
|
}
|
package/src/utils/safe-step.js
CHANGED
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
import * as p from '@clack/prompts'
|
|
2
|
-
import pc from 'picocolors'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Wraps a step function with automatic retry-on-failure UX.
|
|
6
|
-
* If the step throws, shows the error, prompts "Fix it and press Enter to retry",
|
|
7
|
-
* and loops until success or the user cancels.
|
|
8
|
-
*
|
|
9
|
-
* @param {string} label Human-readable step name (shown in spinner)
|
|
10
|
-
* @param {()=>Promise<void>|void} fn The step to execute
|
|
11
|
-
*/
|
|
12
|
-
export async function safeStep(label, fn) {
|
|
13
|
-
while (true) {
|
|
14
|
-
const spinner = p.spinner()
|
|
15
|
-
spinner.start(label)
|
|
16
|
-
try {
|
|
17
|
-
await fn()
|
|
18
|
-
spinner.stop(pc.green(`✓ ${label}`))
|
|
19
|
-
return
|
|
20
|
-
} catch (err) {
|
|
21
|
-
spinner.stop(pc.red(`✗ ${label} failed`))
|
|
22
|
-
|
|
23
|
-
const message = err?.message || String(err)
|
|
24
|
-
p.log.error(pc.red(message))
|
|
25
|
-
p.log.warn(
|
|
26
|
-
pc.yellow(
|
|
27
|
-
`Fix the issue above, then press ${pc.bold('Enter')} to retry — or Ctrl+C to abort.`,
|
|
28
|
-
),
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
const retry = await p.confirm({
|
|
32
|
-
message: 'Ready to retry?',
|
|
33
|
-
initialValue: true,
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
if (p.isCancel(retry) || !retry) {
|
|
37
|
-
p.cancel('Setup aborted.')
|
|
38
|
-
process.exit(1)
|
|
39
|
-
}
|
|
40
|
-
// loop again
|
|
41
|
-
}
|
|
42
|
-
}
|
|
1
|
+
import * as p from '@clack/prompts'
|
|
2
|
+
import pc from 'picocolors'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Wraps a step function with automatic retry-on-failure UX.
|
|
6
|
+
* If the step throws, shows the error, prompts "Fix it and press Enter to retry",
|
|
7
|
+
* and loops until success or the user cancels.
|
|
8
|
+
*
|
|
9
|
+
* @param {string} label Human-readable step name (shown in spinner)
|
|
10
|
+
* @param {()=>Promise<void>|void} fn The step to execute
|
|
11
|
+
*/
|
|
12
|
+
export async function safeStep(label, fn) {
|
|
13
|
+
while (true) {
|
|
14
|
+
const spinner = p.spinner()
|
|
15
|
+
spinner.start(label)
|
|
16
|
+
try {
|
|
17
|
+
await fn()
|
|
18
|
+
spinner.stop(pc.green(`✓ ${label}`))
|
|
19
|
+
return
|
|
20
|
+
} catch (err) {
|
|
21
|
+
spinner.stop(pc.red(`✗ ${label} failed`))
|
|
22
|
+
|
|
23
|
+
const message = err?.message || String(err)
|
|
24
|
+
p.log.error(pc.red(message))
|
|
25
|
+
p.log.warn(
|
|
26
|
+
pc.yellow(
|
|
27
|
+
`Fix the issue above, then press ${pc.bold('Enter')} to retry — or Ctrl+C to abort.`,
|
|
28
|
+
),
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
const retry = await p.confirm({
|
|
32
|
+
message: 'Ready to retry?',
|
|
33
|
+
initialValue: true,
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
if (p.isCancel(retry) || !retry) {
|
|
37
|
+
p.cancel('Setup aborted.')
|
|
38
|
+
process.exit(1)
|
|
39
|
+
}
|
|
40
|
+
// loop again
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
43
|
}
|
package/src/utils/writer.js
CHANGED
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
import fs from 'fs'
|
|
2
|
-
import path from 'path'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Write content to a file, creating parent directories as needed.
|
|
6
|
-
* @param {string} filePath Absolute path
|
|
7
|
-
* @param {string} content
|
|
8
|
-
*/
|
|
9
|
-
export function writeFile(filePath, content) {
|
|
10
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true })
|
|
11
|
-
fs.writeFileSync(filePath, content, 'utf8')
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Append content to an existing file.
|
|
16
|
-
* @param {string} filePath Absolute path
|
|
17
|
-
* @param {string} content
|
|
18
|
-
*/
|
|
19
|
-
export function appendFile(filePath, content) {
|
|
20
|
-
fs.appendFileSync(filePath, content, 'utf8')
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Create a directory (and all parents) if it doesn't exist.
|
|
25
|
-
* @param {string} dirPath Absolute path
|
|
26
|
-
*/
|
|
27
|
-
export function mkdir(dirPath) {
|
|
28
|
-
fs.mkdirSync(dirPath, { recursive: true })
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Check whether a path exists.
|
|
33
|
-
* @param {string} p
|
|
34
|
-
* @returns {boolean}
|
|
35
|
-
*/
|
|
36
|
-
export function exists(p) {
|
|
37
|
-
return fs.existsSync(p)
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Write content to a file, creating parent directories as needed.
|
|
6
|
+
* @param {string} filePath Absolute path
|
|
7
|
+
* @param {string} content
|
|
8
|
+
*/
|
|
9
|
+
export function writeFile(filePath, content) {
|
|
10
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true })
|
|
11
|
+
fs.writeFileSync(filePath, content, 'utf8')
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Append content to an existing file.
|
|
16
|
+
* @param {string} filePath Absolute path
|
|
17
|
+
* @param {string} content
|
|
18
|
+
*/
|
|
19
|
+
export function appendFile(filePath, content) {
|
|
20
|
+
fs.appendFileSync(filePath, content, 'utf8')
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Create a directory (and all parents) if it doesn't exist.
|
|
25
|
+
* @param {string} dirPath Absolute path
|
|
26
|
+
*/
|
|
27
|
+
export function mkdir(dirPath) {
|
|
28
|
+
fs.mkdirSync(dirPath, { recursive: true })
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Check whether a path exists.
|
|
33
|
+
* @param {string} p
|
|
34
|
+
* @returns {boolean}
|
|
35
|
+
*/
|
|
36
|
+
export function exists(p) {
|
|
37
|
+
return fs.existsSync(p)
|
|
38
38
|
}
|