create-nexora-next 0.3.6 → 0.3.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/src/index.js CHANGED
@@ -1,267 +1,267 @@
1
- #!/usr/bin/env node
2
- import * as p from '@clack/prompts'
3
- import pc from 'picocolors'
4
- import path from 'path'
5
- import { existsSync } from 'fs'
6
- import { createRequire } from 'module'
7
-
8
- const require = createRequire(import.meta.url)
9
- const { version } = require('../package.json')
10
-
11
- import { stepCreateNextApp } from './steps/01-create-next-app.js'
12
- import { stepSetupShadcn } from './steps/02-shadcn.js'
13
- import { stepWriteBaseFiles } from './steps/03-base-files.js'
14
- import { stepWriteProviders } from './steps/04-providers.js'
15
- import { stepSetupI18n } from './steps/05-i18n.js'
16
- import { stepSetupAuth } from './steps/06-auth.js'
17
- import { stepWriteProxy } from './steps/07-proxy.js'
18
- import { stepInstallDeps } from './steps/08-install-deps.js'
19
- import { stepSetupHusky } from './steps/09-husky.js'
20
- import { stepSetupAxios } from './steps/10-axios.js'
21
- import { stepPatchPackageJson } from './steps/11-patch-pkg.js'
22
-
23
- // ─── Helpers ──────────────────────────────────────────────────────────────────
24
-
25
- function bail(msg) {
26
- p.cancel(pc.red(msg))
27
- process.exit(1)
28
- }
29
-
30
- function onCancel() {
31
- bail('Setup cancelled.')
32
- }
33
-
34
- function section(title) {
35
- p.log.step(pc.bold(pc.cyan(`\n ${title}`)))
36
- }
37
-
38
- // ─── Banner ───────────────────────────────────────────────────────────────────
39
-
40
- function printBanner() {
41
- const art = [
42
- ' ███╗ ██╗███████╗██╗ ██╗ ██████╗ ██████╗ █████╗ ',
43
- ' ████╗ ██║██╔════╝╚██╗██╔╝██╔═══██╗██╔══██╗██╔══██╗',
44
- ' ██╔██╗ ██║█████╗ ╚███╔╝ ██║ ██║██████╔╝███████║',
45
- ' ██║╚██╗██║██╔══╝ ██╔██╗ ██║ ██║██╔══██╗██╔══██║',
46
- ' ██║ ╚████║███████╗██╔╝ ██╗╚██████╔╝██║ ██║██║ ██║',
47
- ' ╚═╝ ╚═══╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝',
48
- ]
49
- // Fade from cyan → blue top to bottom
50
- const gradient = [pc.cyan, pc.cyan, pc.cyan, pc.blue, pc.blue, pc.blue]
51
-
52
- console.log()
53
- art.forEach((line, i) => console.log(pc.bold(gradient[i](line))))
54
- console.log()
55
- const W = 44
56
- const centre = (str) => {
57
- const pad = W - str.length
58
- const l = Math.floor(pad / 2)
59
- const r = pad - l
60
- return ' '.repeat(l) + str + ' '.repeat(r)
61
- }
62
- const bar = '═'.repeat(W)
63
- console.log(pc.bold(pc.cyan(
64
- ` ╔${bar}╗\n` +
65
- ` ║${centre(`create-nexora-next v${version}`)}║\n` +
66
- ` ║${centre('Next.js scaffold · by Rayan')}║\n` +
67
- ` ╚${bar}╝`
68
- )))
69
- console.log()
70
- }
71
-
72
- // ─── Summary ──────────────────────────────────────────────────────────────────
73
-
74
- function printSummary(opts, projectName, pm) {
75
- const yes = pc.green('✓')
76
- const skip = pc.dim('–')
77
- const row = (label, on) =>
78
- ` ${on ? yes : skip} ${on ? pc.white(label) : pc.dim(label)}`
79
-
80
- console.log()
81
- p.log.success(pc.bold(pc.green(` 🚀 ${projectName} is ready!`)))
82
- console.log()
83
- console.log(pc.bold(' What was set up:'))
84
- console.log(row('Next.js 15 + TypeScript + Tailwind + ESLint', true))
85
- console.log(row('shadcn/ui + custom globals.css utilities', true))
86
- console.log(row('Prettier + tailwind class sorting', true))
87
- console.log(row('Poppins font + path alias ~/', true))
88
- console.log(row('Localization via next-intl', opts.i18n))
89
- console.log(row('TanStack Query + local persistence', opts.query))
90
- console.log(row('Theming with next-themes', opts.theming))
91
- console.log(row('Animations — motion + gsap', opts.animations))
92
- console.log(row('Auth proxy + zod validators', opts.auth))
93
- console.log(row('Axios client + server instances', opts.axios))
94
- console.log(row('Husky pre-commit hooks', opts.husky))
95
- console.log(row('React Compiler', opts.reactCompiler))
96
- console.log()
97
- console.log(pc.bold(' Next steps:'))
98
- console.log(` ${pc.cyan('cd')} ${projectName}`)
99
- const devCmd = pm === 'npm' ? 'npm run dev' : `${pm} dev`
100
- console.log(` ${pc.cyan(devCmd)}`)
101
- console.log()
102
- console.log(pc.dim(' Happy shipping, Rayan. 🔥'))
103
- console.log()
104
- }
105
-
106
- // ─── Main ─────────────────────────────────────────────────────────────────────
107
-
108
- async function main() {
109
- printBanner()
110
- p.intro(pc.bgCyan(pc.black(" create-nexora-next ")))
111
-
112
- // ── Phase 1: Identity ──────────────────────────────────────────────────────
113
- section('Phase 1 — Project identity')
114
-
115
- const projectName = await p.text({
116
- message: 'What is your project name?',
117
- placeholder: 'my-app',
118
- validate: (v) => {
119
- if (!v || !v.trim()) return 'Project name is required.'
120
- if (!/^[a-z0-9-_]+$/.test(v.trim()))
121
- return 'Use lowercase letters, numbers, hyphens or underscores only.'
122
- },
123
- })
124
- if (p.isCancel(projectName)) onCancel()
125
- const name = projectName.trim()
126
-
127
- const targetDir = path.resolve(process.cwd(), name)
128
- if (existsSync(targetDir)) {
129
- bail(`Directory "${name}" already exists. Choose a different name.`)
130
- }
131
-
132
- const pm = await p.select({
133
- message: 'Which package manager?',
134
- options: [
135
- { value: 'pnpm', label: 'pnpm', hint: 'recommended' },
136
- { value: 'bun', label: 'bun', hint: 'fastest' },
137
- { value: 'npm', label: 'npm', hint: 'classic' },
138
- { value: 'yarn', label: 'yarn', hint: 'classic alt' },
139
- ],
140
- })
141
- if (p.isCancel(pm)) onCancel()
142
-
143
- // ── Phase 2: Feature flags ─────────────────────────────────────────────────
144
- section('Phase 2 — Features')
145
-
146
- const i18n = await p.confirm({
147
- message: 'Set up localization? (next-intl)',
148
- initialValue: false,
149
- })
150
- if (p.isCancel(i18n)) onCancel()
151
-
152
- const query = await p.confirm({
153
- message: 'Set up TanStack Query + local persistence?',
154
- initialValue: false,
155
- })
156
- if (p.isCancel(query)) onCancel()
157
-
158
- const theming = await p.confirm({
159
- message: 'Set up theming? (next-themes dark/light)',
160
- initialValue: true,
161
- })
162
- if (p.isCancel(theming)) onCancel()
163
-
164
- const animations = await p.confirm({
165
- message: 'Install animation libraries? (motion + gsap)',
166
- initialValue: false,
167
- })
168
- if (p.isCancel(animations)) onCancel()
169
-
170
- const auth = await p.confirm({
171
- message: 'Set up auth proxy + zod validators?',
172
- initialValue: false,
173
- })
174
- if (p.isCancel(auth)) onCancel()
175
-
176
- const axios = await p.confirm({
177
- message: 'Set up axios client/server instances?',
178
- initialValue: false,
179
- })
180
- if (p.isCancel(axios)) onCancel()
181
-
182
- const husky = await p.confirm({
183
- message: 'Configure Husky pre-commit hooks?',
184
- initialValue: true,
185
- })
186
- if (p.isCancel(husky)) onCancel()
187
-
188
- const reactCompiler = await p.confirm({
189
- message: 'Enable React Compiler? (babel-plugin-react-compiler)',
190
- initialValue: true,
191
- })
192
- if (p.isCancel(reactCompiler)) onCancel()
193
-
194
- // ── Confirm ────────────────────────────────────────────────────────────────
195
- const features = [
196
- i18n && 'i18n',
197
- query && 'query',
198
- theming && 'theming',
199
- animations && 'animations',
200
- auth && 'auth',
201
- axios && 'axios',
202
- husky && 'husky',
203
- reactCompiler && 'react-compiler',
204
- ].filter(Boolean)
205
-
206
- console.log()
207
- p.log.info(
208
- pc.bold(` ${pc.cyan(name)}`) +
209
- pc.dim(` · ${pm}`) +
210
- (features.length ? pc.dim(' · ' + features.join(' · ')) : '')
211
- )
212
- console.log()
213
-
214
- const go = await p.confirm({
215
- message: 'Ready to scaffold?',
216
- initialValue: true,
217
- })
218
- if (p.isCancel(go) || !go) onCancel()
219
-
220
- // ── Phase 3: Execution ─────────────────────────────────────────────────────
221
- section('Phase 3 — Building your project')
222
-
223
- const opts = {
224
- i18n: Boolean(i18n),
225
- query: Boolean(query),
226
- theming: Boolean(theming),
227
- animations: Boolean(animations),
228
- auth: Boolean(auth),
229
- axios: Boolean(axios),
230
- husky: Boolean(husky),
231
- reactCompiler: Boolean(reactCompiler),
232
- }
233
-
234
- await stepCreateNextApp(name, pm, targetDir)
235
- await stepSetupShadcn(targetDir, pm)
236
- await stepWriteBaseFiles(targetDir, opts)
237
- await stepWriteProviders(targetDir, opts)
238
-
239
- if (opts.i18n) {
240
- await stepSetupI18n(targetDir, pm, opts)
241
- }
242
-
243
- if (opts.auth || opts.i18n) {
244
- await stepSetupAuth(targetDir, opts)
245
- }
246
-
247
- await stepWriteProxy(targetDir, opts)
248
-
249
- if (opts.axios) {
250
- await stepSetupAxios(targetDir)
251
- }
252
-
253
- await stepInstallDeps(targetDir, pm, opts)
254
- await stepPatchPackageJson(targetDir)
255
-
256
- if (opts.husky) {
257
- await stepSetupHusky(targetDir, pm)
258
- }
259
-
260
- printSummary(opts, name, pm)
261
- p.outro(pc.bgGreen(pc.black(' Done! ')))
262
- }
263
-
264
- main().catch((err) => {
265
- console.error(pc.red('\n Unexpected error:'), err.message || err)
266
- process.exit(1)
1
+ #!/usr/bin/env node
2
+ import * as p from '@clack/prompts'
3
+ import pc from 'picocolors'
4
+ import path from 'path'
5
+ import { existsSync } from 'fs'
6
+ import { createRequire } from 'module'
7
+
8
+ const require = createRequire(import.meta.url)
9
+ const { version } = require('../package.json')
10
+
11
+ import { stepCreateNextApp } from './steps/01-create-next-app.js'
12
+ import { stepSetupShadcn } from './steps/02-shadcn.js'
13
+ import { stepWriteBaseFiles } from './steps/03-base-files.js'
14
+ import { stepWriteProviders } from './steps/04-providers.js'
15
+ import { stepSetupI18n } from './steps/05-i18n.js'
16
+ import { stepSetupAuth } from './steps/06-auth.js'
17
+ import { stepWriteProxy } from './steps/07-proxy.js'
18
+ import { stepInstallDeps } from './steps/08-install-deps.js'
19
+ import { stepSetupHusky } from './steps/09-husky.js'
20
+ import { stepSetupAxios } from './steps/10-axios.js'
21
+ import { stepPatchPackageJson } from './steps/11-patch-pkg.js'
22
+
23
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
24
+
25
+ function bail(msg) {
26
+ p.cancel(pc.red(msg))
27
+ process.exit(1)
28
+ }
29
+
30
+ function onCancel() {
31
+ bail('Setup cancelled.')
32
+ }
33
+
34
+ function section(title) {
35
+ p.log.step(pc.bold(pc.cyan(`\n ${title}`)))
36
+ }
37
+
38
+ // ─── Banner ───────────────────────────────────────────────────────────────────
39
+
40
+ function printBanner() {
41
+ const art = [
42
+ ' ███╗ ██╗███████╗██╗ ██╗ ██████╗ ██████╗ █████╗ ',
43
+ ' ████╗ ██║██╔════╝╚██╗██╔╝██╔═══██╗██╔══██╗██╔══██╗',
44
+ ' ██╔██╗ ██║█████╗ ╚███╔╝ ██║ ██║██████╔╝███████║',
45
+ ' ██║╚██╗██║██╔══╝ ██╔██╗ ██║ ██║██╔══██╗██╔══██║',
46
+ ' ██║ ╚████║███████╗██╔╝ ██╗╚██████╔╝██║ ██║██║ ██║',
47
+ ' ╚═╝ ╚═══╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝',
48
+ ]
49
+ // Fade from cyan → blue top to bottom
50
+ const gradient = [pc.cyan, pc.cyan, pc.cyan, pc.blue, pc.blue, pc.blue]
51
+
52
+ console.log()
53
+ art.forEach((line, i) => console.log(pc.bold(gradient[i](line))))
54
+ console.log()
55
+ const W = 44
56
+ const centre = (str) => {
57
+ const pad = W - str.length
58
+ const l = Math.floor(pad / 2)
59
+ const r = pad - l
60
+ return ' '.repeat(l) + str + ' '.repeat(r)
61
+ }
62
+ const bar = '═'.repeat(W)
63
+ console.log(pc.bold(pc.cyan(
64
+ ` ╔${bar}╗\n` +
65
+ ` ║${centre(`create-nexora-next v${version}`)}║\n` +
66
+ ` ║${centre('Next.js scaffold · by Rayan')}║\n` +
67
+ ` ╚${bar}╝`
68
+ )))
69
+ console.log()
70
+ }
71
+
72
+ // ─── Summary ──────────────────────────────────────────────────────────────────
73
+
74
+ function printSummary(opts, projectName, pm) {
75
+ const yes = pc.green('✓')
76
+ const skip = pc.dim('–')
77
+ const row = (label, on) =>
78
+ ` ${on ? yes : skip} ${on ? pc.white(label) : pc.dim(label)}`
79
+
80
+ console.log()
81
+ p.log.success(pc.bold(pc.green(` 🚀 ${projectName} is ready!`)))
82
+ console.log()
83
+ console.log(pc.bold(' What was set up:'))
84
+ console.log(row('Next.js 15 + TypeScript + Tailwind + ESLint', true))
85
+ console.log(row('shadcn/ui + custom globals.css utilities', true))
86
+ console.log(row('Prettier + tailwind class sorting', true))
87
+ console.log(row('Poppins font + path alias ~/', true))
88
+ console.log(row('Localization via next-intl', opts.i18n))
89
+ console.log(row('TanStack Query + local persistence', opts.query))
90
+ console.log(row('Theming with next-themes', opts.theming))
91
+ console.log(row('Animations — motion + gsap', opts.animations))
92
+ console.log(row('Auth proxy + zod validators', opts.auth))
93
+ console.log(row('Axios client + server instances', opts.axios))
94
+ console.log(row('Husky pre-commit hooks', opts.husky))
95
+ console.log(row('React Compiler', opts.reactCompiler))
96
+ console.log()
97
+ console.log(pc.bold(' Next steps:'))
98
+ console.log(` ${pc.cyan('cd')} ${projectName}`)
99
+ const devCmd = pm === 'npm' ? 'npm run dev' : `${pm} dev`
100
+ console.log(` ${pc.cyan(devCmd)}`)
101
+ console.log()
102
+ console.log(pc.dim(' Happy shipping, Rayan. 🔥'))
103
+ console.log()
104
+ }
105
+
106
+ // ─── Main ─────────────────────────────────────────────────────────────────────
107
+
108
+ async function main() {
109
+ printBanner()
110
+ p.intro(pc.bgCyan(pc.black(" create-nexora-next ")))
111
+
112
+ // ── Phase 1: Identity ──────────────────────────────────────────────────────
113
+ section('Phase 1 — Project identity')
114
+
115
+ const projectName = await p.text({
116
+ message: 'What is your project name?',
117
+ placeholder: 'my-app',
118
+ validate: (v) => {
119
+ if (!v || !v.trim()) return 'Project name is required.'
120
+ if (!/^[a-z0-9-_]+$/.test(v.trim()))
121
+ return 'Use lowercase letters, numbers, hyphens or underscores only.'
122
+ },
123
+ })
124
+ if (p.isCancel(projectName)) onCancel()
125
+ const name = projectName.trim()
126
+
127
+ const targetDir = path.resolve(process.cwd(), name)
128
+ if (existsSync(targetDir)) {
129
+ bail(`Directory "${name}" already exists. Choose a different name.`)
130
+ }
131
+
132
+ const pm = await p.select({
133
+ message: 'Which package manager?',
134
+ options: [
135
+ { value: 'pnpm', label: 'pnpm', hint: 'recommended' },
136
+ { value: 'bun', label: 'bun', hint: 'fastest' },
137
+ { value: 'npm', label: 'npm', hint: 'classic' },
138
+ { value: 'yarn', label: 'yarn', hint: 'classic alt' },
139
+ ],
140
+ })
141
+ if (p.isCancel(pm)) onCancel()
142
+
143
+ // ── Phase 2: Feature flags ─────────────────────────────────────────────────
144
+ section('Phase 2 — Features')
145
+
146
+ const i18n = await p.confirm({
147
+ message: 'Set up localization? (next-intl)',
148
+ initialValue: false,
149
+ })
150
+ if (p.isCancel(i18n)) onCancel()
151
+
152
+ const query = await p.confirm({
153
+ message: 'Set up TanStack Query + local persistence?',
154
+ initialValue: false,
155
+ })
156
+ if (p.isCancel(query)) onCancel()
157
+
158
+ const theming = await p.confirm({
159
+ message: 'Set up theming? (next-themes dark/light)',
160
+ initialValue: true,
161
+ })
162
+ if (p.isCancel(theming)) onCancel()
163
+
164
+ const animations = await p.confirm({
165
+ message: 'Install animation libraries? (motion + gsap)',
166
+ initialValue: false,
167
+ })
168
+ if (p.isCancel(animations)) onCancel()
169
+
170
+ const auth = await p.confirm({
171
+ message: 'Set up auth proxy + zod validators?',
172
+ initialValue: false,
173
+ })
174
+ if (p.isCancel(auth)) onCancel()
175
+
176
+ const axios = await p.confirm({
177
+ message: 'Set up axios client/server instances?',
178
+ initialValue: false,
179
+ })
180
+ if (p.isCancel(axios)) onCancel()
181
+
182
+ const husky = await p.confirm({
183
+ message: 'Configure Husky pre-commit hooks?',
184
+ initialValue: true,
185
+ })
186
+ if (p.isCancel(husky)) onCancel()
187
+
188
+ const reactCompiler = await p.confirm({
189
+ message: 'Enable React Compiler? (babel-plugin-react-compiler)',
190
+ initialValue: true,
191
+ })
192
+ if (p.isCancel(reactCompiler)) onCancel()
193
+
194
+ // ── Confirm ────────────────────────────────────────────────────────────────
195
+ const features = [
196
+ i18n && 'i18n',
197
+ query && 'query',
198
+ theming && 'theming',
199
+ animations && 'animations',
200
+ auth && 'auth',
201
+ axios && 'axios',
202
+ husky && 'husky',
203
+ reactCompiler && 'react-compiler',
204
+ ].filter(Boolean)
205
+
206
+ console.log()
207
+ p.log.info(
208
+ pc.bold(` ${pc.cyan(name)}`) +
209
+ pc.dim(` · ${pm}`) +
210
+ (features.length ? pc.dim(' · ' + features.join(' · ')) : '')
211
+ )
212
+ console.log()
213
+
214
+ const go = await p.confirm({
215
+ message: 'Ready to scaffold?',
216
+ initialValue: true,
217
+ })
218
+ if (p.isCancel(go) || !go) onCancel()
219
+
220
+ // ── Phase 3: Execution ─────────────────────────────────────────────────────
221
+ section('Phase 3 — Building your project')
222
+
223
+ const opts = {
224
+ i18n: Boolean(i18n),
225
+ query: Boolean(query),
226
+ theming: Boolean(theming),
227
+ animations: Boolean(animations),
228
+ auth: Boolean(auth),
229
+ axios: Boolean(axios),
230
+ husky: Boolean(husky),
231
+ reactCompiler: Boolean(reactCompiler),
232
+ }
233
+
234
+ await stepCreateNextApp(name, pm, targetDir)
235
+ await stepSetupShadcn(targetDir, pm)
236
+ await stepWriteBaseFiles(targetDir, opts)
237
+ await stepWriteProviders(targetDir, opts)
238
+
239
+ if (opts.i18n) {
240
+ await stepSetupI18n(targetDir, pm, opts)
241
+ }
242
+
243
+ if (opts.auth || opts.i18n) {
244
+ await stepSetupAuth(targetDir, opts)
245
+ }
246
+
247
+ await stepWriteProxy(targetDir, opts)
248
+
249
+ if (opts.axios) {
250
+ await stepSetupAxios(targetDir)
251
+ }
252
+
253
+ await stepInstallDeps(targetDir, pm, opts)
254
+ await stepPatchPackageJson(targetDir)
255
+
256
+ if (opts.husky) {
257
+ await stepSetupHusky(targetDir, pm)
258
+ }
259
+
260
+ printSummary(opts, name, pm)
261
+ p.outro(pc.bgGreen(pc.black(' Done! ')))
262
+ }
263
+
264
+ main().catch((err) => {
265
+ console.error(pc.red('\n Unexpected error:'), err.message || err)
266
+ process.exit(1)
267
267
  })
@@ -1,19 +1,19 @@
1
- import path from 'path'
2
- import { run, execCmd } from '../utils/runner.js'
3
- import { safeStep } from '../utils/safe-step.js'
4
-
5
- /**
6
- * @param {string} projectName
7
- * @param {'npm'|'pnpm'|'bun'|'yarn'} pm
8
- * @param {string} targetDir Absolute path where the project will live
9
- */
10
- export async function stepCreateNextApp(projectName, pm, targetDir) {
11
- await safeStep('Scaffolding Next.js app', () => {
12
- const x = execCmd(pm)
13
- // create-next-app flags: TypeScript, Tailwind, ESLint, App Router, src dir, no Turbopack prompt, import alias ~
14
- run(
15
- `${x} create-next-app@latest ${projectName} --typescript --tailwind --eslint --app --src-dir --no-turbopack --import-alias "~/*" --use-${pm === 'npm' ? 'npm' : pm}`,
16
- path.dirname(targetDir),
17
- )
18
- })
1
+ import path from 'path'
2
+ import { run, execCmd } from '../utils/runner.js'
3
+ import { safeStep } from '../utils/safe-step.js'
4
+
5
+ /**
6
+ * @param {string} projectName
7
+ * @param {'npm'|'pnpm'|'bun'|'yarn'} pm
8
+ * @param {string} targetDir Absolute path where the project will live
9
+ */
10
+ export async function stepCreateNextApp(projectName, pm, targetDir) {
11
+ await safeStep('Scaffolding Next.js app', () => {
12
+ const x = execCmd(pm)
13
+ // create-next-app flags: TypeScript, Tailwind, ESLint, App Router, src dir, no Turbopack prompt, import alias ~
14
+ run(
15
+ `${x} create-next-app@latest ${projectName} --typescript --tailwind --eslint --app --src-dir --no-turbopack --import-alias "~/*" --use-${pm === 'npm' ? 'npm' : pm}`,
16
+ path.dirname(targetDir),
17
+ )
18
+ })
19
19
  }
@@ -1,39 +1,39 @@
1
- import path from 'path'
2
- import fs from 'fs'
3
- import { run, execCmd } from '../utils/runner.js'
4
- import { appendFile } from '../utils/writer.js'
5
- import { safeStep } from '../utils/safe-step.js'
6
- import { globalsCssAppend } from '../templates/files.js'
7
-
8
- /**
9
- * @param {string} targetDir
10
- * @param {'npm'|'pnpm'|'bun'|'yarn'} pm
11
- */
12
- export async function stepSetupShadcn(targetDir, pm) {
13
- await safeStep('Initialising shadcn/ui', () => {
14
- const x = execCmd(pm)
15
- run(`${x} shadcn@latest init -y -d`, targetDir)
16
- })
17
-
18
- await safeStep('Patching shadcn config + cleaning up', () => {
19
- // 1. Delete src/lib/utils.ts if shadcn created it (we use src/lib/utils/index.ts)
20
- const shadcnUtils = path.join(targetDir, 'src', 'lib', 'utils.ts')
21
- if (fs.existsSync(shadcnUtils)) fs.unlinkSync(shadcnUtils)
22
-
23
- // 2. Update components.json utils path to point at our index.ts
24
- const componentsJson = path.join(targetDir, 'components.json')
25
- if (fs.existsSync(componentsJson)) {
26
- const raw = fs.readFileSync(componentsJson, 'utf8')
27
- const patched = raw.replace(
28
- '"utils": "~/lib/utils"',
29
- '"utils": "~/lib/utils/index.ts"',
30
- )
31
- fs.writeFileSync(componentsJson, patched, 'utf8')
32
- }
33
- })
34
-
35
- await safeStep('Appending custom styles to globals.css', () => {
36
- const globalsPath = path.join(targetDir, 'src', 'app', 'globals.css')
37
- appendFile(globalsPath, globalsCssAppend)
38
- })
1
+ import path from 'path'
2
+ import fs from 'fs'
3
+ import { run, execCmd } from '../utils/runner.js'
4
+ import { appendFile } from '../utils/writer.js'
5
+ import { safeStep } from '../utils/safe-step.js'
6
+ import { globalsCssAppend } from '../templates/files.js'
7
+
8
+ /**
9
+ * @param {string} targetDir
10
+ * @param {'npm'|'pnpm'|'bun'|'yarn'} pm
11
+ */
12
+ export async function stepSetupShadcn(targetDir, pm) {
13
+ await safeStep('Initialising shadcn/ui', () => {
14
+ const x = execCmd(pm)
15
+ run(`${x} shadcn@latest init -y -d`, targetDir)
16
+ })
17
+
18
+ await safeStep('Patching shadcn config + cleaning up', () => {
19
+ // 1. Delete src/lib/utils.ts if shadcn created it (we use src/lib/utils/index.ts)
20
+ const shadcnUtils = path.join(targetDir, 'src', 'lib', 'utils.ts')
21
+ if (fs.existsSync(shadcnUtils)) fs.unlinkSync(shadcnUtils)
22
+
23
+ // 2. Update components.json utils path to point at our index.ts
24
+ const componentsJson = path.join(targetDir, 'components.json')
25
+ if (fs.existsSync(componentsJson)) {
26
+ const raw = fs.readFileSync(componentsJson, 'utf8')
27
+ const patched = raw.replace(
28
+ '"utils": "~/lib/utils"',
29
+ '"utils": "~/lib/utils/index.ts"',
30
+ )
31
+ fs.writeFileSync(componentsJson, patched, 'utf8')
32
+ }
33
+ })
34
+
35
+ await safeStep('Appending custom styles to globals.css', () => {
36
+ const globalsPath = path.join(targetDir, 'src', 'app', 'globals.css')
37
+ appendFile(globalsPath, globalsCssAppend)
38
+ })
39
39
  }