create-nextjs-cms 0.5.63 → 0.5.66
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/dist/index.js +50 -198
- package/dist/lib/cms-setup.d.ts +6 -0
- package/dist/lib/cms-setup.d.ts.map +1 -0
- package/dist/lib/cms-setup.js +30 -0
- package/dist/lib/exec-utils.d.ts +28 -0
- package/dist/lib/exec-utils.d.ts.map +1 -0
- package/dist/lib/exec-utils.js +61 -0
- package/dist/lib/global-package.d.ts +10 -0
- package/dist/lib/global-package.d.ts.map +1 -0
- package/dist/lib/global-package.js +158 -0
- package/dist/lib/install-deps.d.ts +4 -0
- package/dist/lib/install-deps.d.ts.map +1 -0
- package/dist/lib/install-deps.js +114 -0
- package/dist/lib/logger.d.ts +8 -0
- package/dist/lib/logger.d.ts.map +1 -0
- package/dist/lib/logger.js +18 -0
- package/dist/lib/section-creators.d.ts +13 -0
- package/dist/lib/section-creators.d.ts.map +1 -0
- package/dist/lib/section-creators.js +201 -0
- package/dist/lib/utils.js +1 -1
- package/package.json +4 -2
- package/templates/default/lib/postinstall.js +1 -1
- package/templates/default/package.json +1 -1
- package/templates/default/.uploads/.photos/categories/000e56fada378de1a84a4 +0 -0
- package/templates/default/.uploads/.photos/categories/121e7d13ee3b5fa03795b +0 -0
- package/templates/default/.uploads/.photos/categories/17a55c5a53eb62293c69e +0 -0
- package/templates/default/.uploads/.photos/categories/4c86ad8c1f51f1e3a953b +0 -0
- package/templates/default/.uploads/.photos/categories/4d360813741a45744327c +0 -0
- package/templates/default/.uploads/.photos/categories/5238fefaa3cbebf388178 +0 -0
- package/templates/default/.uploads/.photos/categories/541d7cf8c69895bcb15cd +0 -0
- package/templates/default/.uploads/.photos/categories/57a2f689a910a378247ea +0 -0
- package/templates/default/.uploads/.photos/categories/6de4bdb341a3e1f70ddc3 +0 -0
- package/templates/default/.uploads/.photos/categories/6f52739eaa686441a28f4 +0 -0
- package/templates/default/.uploads/.photos/categories/8a21b9fa8ecd88f460a15 +0 -0
- package/templates/default/.uploads/.photos/categories/91948abcb073c9445fdec +0 -0
- package/templates/default/.uploads/.photos/categories/9ae700d1abfd6b85780e8 +0 -0
- package/templates/default/.uploads/.photos/categories/9edee86e932985fc589b5 +0 -0
- package/templates/default/.uploads/.photos/categories/a520e77b082f35b575dba +0 -0
- package/templates/default/.uploads/.photos/categories/b19c449029330f0a74b20 +0 -0
- package/templates/default/.uploads/.photos/categories/b9802010f68afd4edb0e8 +0 -0
- package/templates/default/.uploads/.photos/categories/c1d7c3b986739bf496730 +0 -0
- package/templates/default/.uploads/.photos/categories/c25dc38567384513ffe93 +0 -0
- package/templates/default/.uploads/.photos/categories/cbe3874a3d13afba388df +0 -0
- package/templates/default/.uploads/.photos/categories/d450fdeb04f0d070442d6 +0 -0
- package/templates/default/.uploads/.photos/categories/f80bf3a4515680ead5a5c +0 -0
- package/templates/default/.uploads/.photos/categories/f8639c2d5b0d24cb76fb1 +0 -0
- package/templates/default/.uploads/.photos/categories/f8d997149d10aab046e40 +0 -0
- package/templates/default/.uploads/.photos/categories/fa2c55690ff96e33a16fe +0 -0
- package/templates/default/.uploads/.photos/featured_slider/d00be4edb4c38ca34b5a5 +0 -0
- package/templates/default/.uploads/.thumbs/categories/000e56fada378de1a84a4 +0 -0
- package/templates/default/.uploads/.thumbs/categories/121e7d13ee3b5fa03795b +0 -0
- package/templates/default/.uploads/.thumbs/categories/17a55c5a53eb62293c69e +0 -0
- package/templates/default/.uploads/.thumbs/categories/4c86ad8c1f51f1e3a953b +0 -0
- package/templates/default/.uploads/.thumbs/categories/4d360813741a45744327c +0 -0
- package/templates/default/.uploads/.thumbs/categories/5238fefaa3cbebf388178 +0 -0
- package/templates/default/.uploads/.thumbs/categories/541d7cf8c69895bcb15cd +0 -0
- package/templates/default/.uploads/.thumbs/categories/57a2f689a910a378247ea +0 -0
- package/templates/default/.uploads/.thumbs/categories/6de4bdb341a3e1f70ddc3 +0 -0
- package/templates/default/.uploads/.thumbs/categories/6f52739eaa686441a28f4 +0 -0
- package/templates/default/.uploads/.thumbs/categories/8a21b9fa8ecd88f460a15 +0 -0
- package/templates/default/.uploads/.thumbs/categories/91948abcb073c9445fdec +0 -0
- package/templates/default/.uploads/.thumbs/categories/9ae700d1abfd6b85780e8 +0 -0
- package/templates/default/.uploads/.thumbs/categories/9edee86e932985fc589b5 +0 -0
- package/templates/default/.uploads/.thumbs/categories/a520e77b082f35b575dba +0 -0
- package/templates/default/.uploads/.thumbs/categories/b19c449029330f0a74b20 +0 -0
- package/templates/default/.uploads/.thumbs/categories/b9802010f68afd4edb0e8 +0 -0
- package/templates/default/.uploads/.thumbs/categories/c1d7c3b986739bf496730 +0 -0
- package/templates/default/.uploads/.thumbs/categories/c25dc38567384513ffe93 +0 -0
- package/templates/default/.uploads/.thumbs/categories/cbe3874a3d13afba388df +0 -0
- package/templates/default/.uploads/.thumbs/categories/d450fdeb04f0d070442d6 +0 -0
- package/templates/default/.uploads/.thumbs/categories/f80bf3a4515680ead5a5c +0 -0
- package/templates/default/.uploads/.thumbs/categories/f8639c2d5b0d24cb76fb1 +0 -0
- package/templates/default/.uploads/.thumbs/categories/f8d997149d10aab046e40 +0 -0
- package/templates/default/.uploads/.thumbs/categories/fa2c55690ff96e33a16fe +0 -0
package/dist/index.js
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import fs from 'fs-extra';
|
|
3
|
-
import { execSync } from 'node:child_process';
|
|
4
3
|
import { fileURLToPath } from 'node:url';
|
|
5
4
|
import { randomBytes } from 'node:crypto';
|
|
6
5
|
import path, { dirname, resolve, relative, basename } from 'node:path';
|
|
7
6
|
import { Command } from 'commander';
|
|
8
|
-
import { text,
|
|
7
|
+
import { text, log } from '@clack/prompts';
|
|
9
8
|
import { expandHome, isValidPkgName, isEmptyDir, detectPackageManager, validateTemplate } from './lib/utils.js';
|
|
10
9
|
import { readFileSync } from 'node:fs';
|
|
11
10
|
import chalk from 'chalk';
|
|
12
11
|
import { renderTitle } from './lib/render-title.js';
|
|
12
|
+
import { installDependencies } from './lib/install-deps.js';
|
|
13
|
+
import { runCmsSetup } from './lib/cms-setup.js';
|
|
14
|
+
import { removeGlobalPackage, installGlobalPackage } from './lib/global-package.js';
|
|
15
|
+
import { createBlogSection, createCategorySection, createSimpleSection } from './lib/section-creators.js';
|
|
13
16
|
/** Resolve __dirname for ESM */
|
|
14
17
|
const __filename = fileURLToPath(import.meta.url);
|
|
15
18
|
const __dirname = dirname(__filename);
|
|
@@ -23,80 +26,6 @@ const templateDir = fileURLToPath(new URL('../templates/default/', import.meta.u
|
|
|
23
26
|
const handleSigTerm = () => process.exit(0);
|
|
24
27
|
process.on('SIGINT', handleSigTerm);
|
|
25
28
|
process.on('SIGTERM', handleSigTerm);
|
|
26
|
-
/**
|
|
27
|
-
* Creates a blog section file in the sections folder
|
|
28
|
-
*/
|
|
29
|
-
async function createBlogSection(targetDir) {
|
|
30
|
-
const sectionsDir = path.join(targetDir, 'sections');
|
|
31
|
-
const blogSectionPath = path.join(sectionsDir, 'blog.section.ts');
|
|
32
|
-
// Ensure sections directory exists
|
|
33
|
-
await fs.ensureDir(sectionsDir);
|
|
34
|
-
const blogSectionContent = `import { photoField, richTextField, textField, tagsField } from 'nextjs-cms/core/fields'
|
|
35
|
-
import { hasItemsSection } from 'nextjs-cms/core/sections'
|
|
36
|
-
|
|
37
|
-
const title = textField({
|
|
38
|
-
name: 'title',
|
|
39
|
-
label: 'Title',
|
|
40
|
-
required: true,
|
|
41
|
-
order: 1,
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
const coverPhotoField = photoField({
|
|
45
|
-
name: 'coverphoto',
|
|
46
|
-
label: 'Cover Photo',
|
|
47
|
-
required: true,
|
|
48
|
-
order: 2,
|
|
49
|
-
fileType: ['jpg', 'jpeg', 'png', 'webp'],
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
const content = richTextField({
|
|
53
|
-
name: 'content',
|
|
54
|
-
label: 'Content',
|
|
55
|
-
required: true,
|
|
56
|
-
order: 3,
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const keywords = tagsField({
|
|
61
|
-
name: 'keywords',
|
|
62
|
-
label: 'Keywords',
|
|
63
|
-
required: false,
|
|
64
|
-
order: 4,
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
export default hasItemsSection({
|
|
68
|
-
name: 'blog',
|
|
69
|
-
order: 1,
|
|
70
|
-
icon: 'icon',
|
|
71
|
-
readonly: false,
|
|
72
|
-
headingField: title,
|
|
73
|
-
title: {
|
|
74
|
-
section: 'Blog',
|
|
75
|
-
singular: 'Post',
|
|
76
|
-
plural: 'Posts',
|
|
77
|
-
},
|
|
78
|
-
gallery: {
|
|
79
|
-
db: {
|
|
80
|
-
tableName: 'blog_gallery',
|
|
81
|
-
identifierField: 'reference_id',
|
|
82
|
-
photoNameField: 'photo',
|
|
83
|
-
metaField: 'meta',
|
|
84
|
-
},
|
|
85
|
-
},
|
|
86
|
-
db: {
|
|
87
|
-
table: 'blog',
|
|
88
|
-
},
|
|
89
|
-
search: {
|
|
90
|
-
searchFields: [title],
|
|
91
|
-
},
|
|
92
|
-
coverPhotoField: coverPhotoField,
|
|
93
|
-
generateQR: false,
|
|
94
|
-
fields: [title, coverPhotoField, content, keywords],
|
|
95
|
-
})
|
|
96
|
-
`;
|
|
97
|
-
await fs.writeFile(blogSectionPath, blogSectionContent, 'utf-8');
|
|
98
|
-
log.success('Blog section created successfully!');
|
|
99
|
-
}
|
|
100
29
|
/**
|
|
101
30
|
* Generates a random secret string using crypto.randomBytes
|
|
102
31
|
*/
|
|
@@ -121,7 +50,7 @@ async function updateEnvSecrets(targetDir) {
|
|
|
121
50
|
envContent = envContent.replace(/ACCESS_TOKEN_EXPIRATION=.*/, 'ACCESS_TOKEN_EXPIRATION=2h');
|
|
122
51
|
envContent = envContent.replace(/REFRESH_TOKEN_EXPIRATION=.*/, 'REFRESH_TOKEN_EXPIRATION=1y');
|
|
123
52
|
await fs.writeFile(envPath, envContent, 'utf-8');
|
|
124
|
-
log.
|
|
53
|
+
log.message('Generated random secrets in .env file');
|
|
125
54
|
}
|
|
126
55
|
catch (e) {
|
|
127
56
|
log.warn('Could not update .env secrets automatically.');
|
|
@@ -130,8 +59,7 @@ async function updateEnvSecrets(targetDir) {
|
|
|
130
59
|
}
|
|
131
60
|
async function createNextjsCms() {
|
|
132
61
|
renderTitle();
|
|
133
|
-
log.info('
|
|
134
|
-
log.message('Creating your new CMS project...\n');
|
|
62
|
+
log.info('Create your new NextJS CMS project');
|
|
135
63
|
let projectPath = '';
|
|
136
64
|
const program = new Command(packageJson.name)
|
|
137
65
|
.version(packageJson.version, '-v, --version', 'Output the current version of create-nextjs-cms.')
|
|
@@ -152,14 +80,14 @@ async function createNextjsCms() {
|
|
|
152
80
|
targetDir: '',
|
|
153
81
|
projectName: '',
|
|
154
82
|
templateDir: templateDir,
|
|
155
|
-
|
|
83
|
+
sectionsToAdd: [],
|
|
156
84
|
};
|
|
157
85
|
let targetIsCwd = false;
|
|
158
86
|
try {
|
|
159
87
|
// Ensure template folder is present in the published package
|
|
160
88
|
if (!(await fs.pathExists(templateDir))) {
|
|
161
89
|
log.error('Template directory not found in the published package.');
|
|
162
|
-
log.message('
|
|
90
|
+
log.message('Please open an issue on the create-nextjs-cms repository: https://github.com/next-cms/create-nextjs-cms/issues');
|
|
163
91
|
process.exit(1);
|
|
164
92
|
}
|
|
165
93
|
// Validate template structure
|
|
@@ -168,6 +96,7 @@ async function createNextjsCms() {
|
|
|
168
96
|
if (!projectPath) {
|
|
169
97
|
const res = await text({
|
|
170
98
|
message: 'What is your project named?',
|
|
99
|
+
placeholder: 'my-cms-app',
|
|
171
100
|
defaultValue: 'my-cms-app',
|
|
172
101
|
validate: (name) => {
|
|
173
102
|
const validation = isValidPkgName(basename(resolve(name)));
|
|
@@ -227,17 +156,20 @@ async function createNextjsCms() {
|
|
|
227
156
|
await fs.ensureDir(options.targetDir);
|
|
228
157
|
}
|
|
229
158
|
}
|
|
230
|
-
// Ask
|
|
231
|
-
const
|
|
232
|
-
|
|
233
|
-
|
|
159
|
+
// Ask which sections to add
|
|
160
|
+
const { multiselect } = await import('@clack/prompts');
|
|
161
|
+
const sectionsRes = await multiselect({
|
|
162
|
+
message: 'Would you like to add default sections?',
|
|
163
|
+
options: [
|
|
164
|
+
{ value: 'blog', label: 'Blog' },
|
|
165
|
+
{ value: 'category', label: 'Category' },
|
|
166
|
+
{ value: 'simple', label: 'Settings' },
|
|
167
|
+
],
|
|
168
|
+
required: false,
|
|
234
169
|
});
|
|
235
|
-
options.
|
|
170
|
+
options.sectionsToAdd = sectionsRes;
|
|
236
171
|
log.step(`Creating project in: ${options.targetDir}`);
|
|
237
172
|
// Copy template → project dir with a filter to skip build/cache artifacts
|
|
238
|
-
const copySpinner = spinner();
|
|
239
|
-
log.step('🔄 Copying template files...');
|
|
240
|
-
copySpinner.start();
|
|
241
173
|
await fs.copy(options.templateDir, options.targetDir, {
|
|
242
174
|
filter: (src) => {
|
|
243
175
|
const rel = relative(options.templateDir, src);
|
|
@@ -253,7 +185,6 @@ async function createNextjsCms() {
|
|
|
253
185
|
overwrite: true,
|
|
254
186
|
errorOnExist: false,
|
|
255
187
|
});
|
|
256
|
-
copySpinner.stop('✅ Template copied successfully!');
|
|
257
188
|
// Generate random secrets for .env file
|
|
258
189
|
await updateEnvSecrets(options.targetDir);
|
|
259
190
|
// npm excludes .gitignore from packages, so we rename it back from _gitignore
|
|
@@ -261,12 +192,23 @@ async function createNextjsCms() {
|
|
|
261
192
|
const gitignorePath = path.join(options.targetDir, '.gitignore');
|
|
262
193
|
if (await fs.pathExists(gitignoreTemplatePath)) {
|
|
263
194
|
await fs.move(gitignoreTemplatePath, gitignorePath);
|
|
264
|
-
log.step('Restored .gitignore file');
|
|
265
195
|
}
|
|
266
|
-
// Create
|
|
267
|
-
if (options.
|
|
268
|
-
|
|
269
|
-
|
|
196
|
+
// Create selected sections
|
|
197
|
+
if (options.sectionsToAdd.length > 0) {
|
|
198
|
+
for (const section of options.sectionsToAdd) {
|
|
199
|
+
switch (section) {
|
|
200
|
+
case 'blog':
|
|
201
|
+
await createBlogSection(options.targetDir);
|
|
202
|
+
break;
|
|
203
|
+
case 'category':
|
|
204
|
+
await createCategorySection(options.targetDir);
|
|
205
|
+
break;
|
|
206
|
+
case 'simple':
|
|
207
|
+
await createSimpleSection(options.targetDir);
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
log.message(`${options.sectionsToAdd.join(', ')} section${options.sectionsToAdd.length > 1 ? 's' : ''} added successfully`);
|
|
270
212
|
}
|
|
271
213
|
// Update package.json name (if template contains package.json)
|
|
272
214
|
const packageJsonPath = path.join(options.targetDir, 'package.json');
|
|
@@ -275,123 +217,33 @@ async function createNextjsCms() {
|
|
|
275
217
|
const pkg = (await fs.readJson(packageJsonPath));
|
|
276
218
|
pkg.name = options.projectName;
|
|
277
219
|
await fs.writeJson(packageJsonPath, pkg, { spaces: 2 });
|
|
278
|
-
log.step(`Updated package.json name to: ${options.projectName}`);
|
|
279
220
|
}
|
|
280
|
-
catch
|
|
221
|
+
catch {
|
|
281
222
|
log.warn('Could not update package.json name automatically.');
|
|
282
|
-
log.error(` Error: ${e instanceof Error ? e.message : 'Unknown error'}`);
|
|
283
223
|
}
|
|
284
224
|
}
|
|
285
225
|
else {
|
|
286
226
|
log.warn('No package.json found in template root; skipping name update.');
|
|
287
227
|
}
|
|
288
|
-
log.info('Installing dependencies...');
|
|
289
228
|
process.chdir(options.targetDir);
|
|
290
|
-
const installSpinner = spinner();
|
|
291
|
-
installSpinner.start('Installing dependencies...');
|
|
292
229
|
const preferredPM = detectPackageManager();
|
|
293
|
-
log.step(`Using ${chalk.green(preferredPM)} as the package manager...`);
|
|
294
230
|
let installed = false;
|
|
295
|
-
// Try preferred package manager first
|
|
296
|
-
try {
|
|
297
|
-
if (preferredPM === 'pnpm') {
|
|
298
|
-
execSync('pnpm install', { stdio: 'inherit' });
|
|
299
|
-
installed = true;
|
|
300
|
-
installSpinner.stop('✅ Dependencies installed with pnpm!');
|
|
301
|
-
}
|
|
302
|
-
else if (preferredPM === 'yarn') {
|
|
303
|
-
execSync('yarn install', { stdio: 'inherit' });
|
|
304
|
-
installed = true;
|
|
305
|
-
installSpinner.stop('✅ Dependencies installed with yarn!');
|
|
306
|
-
}
|
|
307
|
-
else if (preferredPM === 'bun') {
|
|
308
|
-
execSync('bun install', { stdio: 'inherit' });
|
|
309
|
-
installed = true;
|
|
310
|
-
installSpinner.stop('✅ Dependencies installed with bun!');
|
|
311
|
-
}
|
|
312
|
-
else {
|
|
313
|
-
execSync('npm install', { stdio: 'inherit' });
|
|
314
|
-
installed = true;
|
|
315
|
-
installSpinner.stop('✅ Dependencies installed with npm!');
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
catch {
|
|
319
|
-
installSpinner.message(`❌ ${preferredPM} install failed. Trying alternatives...`);
|
|
320
|
-
// Fallback to other package managers
|
|
321
|
-
const fallbacks = preferredPM === 'pnpm'
|
|
322
|
-
? ['npm', 'yarn', 'bun']
|
|
323
|
-
: preferredPM === 'yarn'
|
|
324
|
-
? ['npm', 'pnpm', 'bun']
|
|
325
|
-
: ['pnpm', 'yarn', 'bun'];
|
|
326
|
-
for (const pm of fallbacks) {
|
|
327
|
-
try {
|
|
328
|
-
execSync(`${pm} install`, { stdio: 'inherit' });
|
|
329
|
-
installed = true;
|
|
330
|
-
installSpinner.stop(`✅ Dependencies installed with ${pm}!`);
|
|
331
|
-
break;
|
|
332
|
-
}
|
|
333
|
-
catch {
|
|
334
|
-
installSpinner.message(`❌ ${pm} install failed.`);
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
if (!installed) {
|
|
338
|
-
installSpinner.stop('❌ Failed to install dependencies automatically.');
|
|
339
|
-
log.error(' Please run "pnpm install", "npm install", "yarn install", or "bun install" manually.');
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
// Remove any existing global nextjs-cms-kit installation before installing with preferred PM
|
|
343
|
-
log.step('Ensuring clean nextjs-cms-kit global installation...');
|
|
344
|
-
const globalRemoveCommands = {
|
|
345
|
-
pnpm: 'pnpm remove -g nextjs-cms-kit',
|
|
346
|
-
npm: 'npm uninstall -g nextjs-cms-kit',
|
|
347
|
-
yarn: 'yarn global remove nextjs-cms-kit',
|
|
348
|
-
bun: 'bun remove -g nextjs-cms-kit',
|
|
349
|
-
};
|
|
350
|
-
// Try to remove from all package managers to ensure clean state
|
|
351
|
-
const allPMs = ['pnpm', 'npm', 'yarn', 'bun'];
|
|
352
|
-
for (const pm of allPMs) {
|
|
353
|
-
try {
|
|
354
|
-
execSync(globalRemoveCommands[pm], { stdio: 'pipe' });
|
|
355
|
-
}
|
|
356
|
-
catch {
|
|
357
|
-
// Ignore errors - package might not be installed with this PM
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
// Install nextjs-cms-kit globally for setup
|
|
361
|
-
log.step('Installing nextjs-cms-kit globally...');
|
|
362
231
|
try {
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
232
|
+
log.step(`Using ${chalk.green(preferredPM)} as the package manager...`);
|
|
233
|
+
// Install dependencies first
|
|
234
|
+
await installDependencies({ projectDir: options.targetDir });
|
|
235
|
+
installed = true;
|
|
236
|
+
// Then remove any existing global package
|
|
237
|
+
await removeGlobalPackage('nextjs-cms-kit');
|
|
238
|
+
// Install the global package
|
|
239
|
+
await installGlobalPackage('nextjs-cms-kit', packageJson.version);
|
|
240
|
+
// Finally run CMS setup
|
|
241
|
+
await runCmsSetup({ dir: options.targetDir, installed, preferredPM });
|
|
371
242
|
}
|
|
372
243
|
catch (error) {
|
|
373
|
-
log.
|
|
374
|
-
|
|
375
|
-
}
|
|
376
|
-
// Run CMS setup
|
|
377
|
-
const setupSpinner = spinner();
|
|
378
|
-
setupSpinner.start('Running CMS setup...');
|
|
379
|
-
try {
|
|
380
|
-
execSync('nextjs-cms-kit --dev setup', { stdio: 'inherit' });
|
|
381
|
-
setupSpinner.stop('✅ CMS setup completed successfully!');
|
|
382
|
-
}
|
|
383
|
-
catch (error) {
|
|
384
|
-
setupSpinner.stop('⚠️ CMS setup failed. You may need to run it manually:');
|
|
385
|
-
log.warn(' nextjs-cms-kit --dev setup');
|
|
386
|
-
log.error(` Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
244
|
+
log.error(`❌ Failed to complete project setup: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
245
|
+
process.exit(1);
|
|
387
246
|
}
|
|
388
|
-
log.success('\n🎉 Your NextJS CMS project has been created successfully!');
|
|
389
|
-
log.message('\nNext steps:');
|
|
390
|
-
log.message(` cd ${options.projectName}`);
|
|
391
|
-
log.message(installed
|
|
392
|
-
? ` ${preferredPM} dev # or: npm run dev`
|
|
393
|
-
: ` ${preferredPM} install && ${preferredPM} dev # or: npm install && npm run dev`);
|
|
394
|
-
log.message('\nHappy coding! 🚀');
|
|
395
247
|
}
|
|
396
248
|
catch (error) {
|
|
397
249
|
log.error(`❌ Failed to create project: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cms-setup.d.ts","sourceRoot":"","sources":["../../src/lib/cms-setup.ts"],"names":[],"mappings":"AAmBA,eAAO,MAAM,WAAW,GAAU,kCAI/B;IACC,GAAG,EAAE,MAAM,CAAA;IACX,SAAS,EAAE,OAAO,CAAA;IAClB,WAAW,EAAE,MAAM,CAAA;CACtB,kBAcA,CAAA"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { logger } from './logger.js';
|
|
3
|
+
import { execWithoutSpinner } from './exec-utils.js';
|
|
4
|
+
import { log } from '@clack/prompts';
|
|
5
|
+
const runCmsSetupCommand = async (projectDir) => {
|
|
6
|
+
// For all package managers, use inherit for stdout/stderr to avoid hanging issues
|
|
7
|
+
// The command will show its own output and the spinner will just indicate progress
|
|
8
|
+
await execWithoutSpinner('nextjs-cms-kit', ['--dev', 'setup'], {
|
|
9
|
+
cwd: projectDir,
|
|
10
|
+
stdout: 'inherit',
|
|
11
|
+
stderr: 'inherit',
|
|
12
|
+
});
|
|
13
|
+
return null;
|
|
14
|
+
};
|
|
15
|
+
export const runCmsSetup = async ({ dir, installed, preferredPM, }) => {
|
|
16
|
+
log.info(chalk.cyan('Running CMS setup...'));
|
|
17
|
+
try {
|
|
18
|
+
await runCmsSetupCommand(dir);
|
|
19
|
+
logger.success('\n🎉 Your NextJS CMS project has been created successfully!');
|
|
20
|
+
logger.message('\nNext steps:');
|
|
21
|
+
logger.message(` cd ${dir}`);
|
|
22
|
+
logger.message(installed ? ` ${preferredPM} dev` : ` ${preferredPM} install && ${preferredPM} dev`);
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
logger.error('⚠️ CMS setup failed. You may need to run it manually:');
|
|
26
|
+
logger.warn(' nextjs-cms-kit --dev setup');
|
|
27
|
+
logger.error(` Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
28
|
+
throw error;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Ora } from 'ora';
|
|
2
|
+
export interface ExecWithSpinnerOptions {
|
|
3
|
+
command: string;
|
|
4
|
+
args: string[];
|
|
5
|
+
cwd?: string;
|
|
6
|
+
stdout?: 'pipe' | 'ignore' | 'inherit';
|
|
7
|
+
stderr?: 'pipe' | 'ignore' | 'inherit';
|
|
8
|
+
spinnerText?: string;
|
|
9
|
+
onDataHandle?: (spinner: Ora) => (data: Buffer) => void;
|
|
10
|
+
onStderrHandle?: (data: Buffer) => void;
|
|
11
|
+
checkExitCode?: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Execute a command with a spinner, handling different stdio configurations
|
|
15
|
+
* and optional data handlers for progress updates.
|
|
16
|
+
*/
|
|
17
|
+
export declare const execWithSpinner: (options: ExecWithSpinnerOptions) => Promise<Ora | null>;
|
|
18
|
+
/**
|
|
19
|
+
* Execute a command without a spinner (for cases where we want direct output)
|
|
20
|
+
*/
|
|
21
|
+
export declare const execWithoutSpinner: (command: string, args: string[], options?: {
|
|
22
|
+
cwd?: string;
|
|
23
|
+
stdout?: "pipe" | "ignore" | "inherit";
|
|
24
|
+
stderr?: "pipe" | "ignore" | "inherit";
|
|
25
|
+
reject?: boolean;
|
|
26
|
+
env?: Record<string, string>;
|
|
27
|
+
}) => Promise<void>;
|
|
28
|
+
//# sourceMappingURL=exec-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec-utils.d.ts","sourceRoot":"","sources":["../../src/lib/exec-utils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAE9B,MAAM,WAAW,sBAAsB;IACnC,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAA;IACtC,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAA;IACtC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACvD,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACvC,aAAa,CAAC,EAAE,OAAO,CAAA;CAC1B;AAED;;;GAGG;AACH,eAAO,MAAM,eAAe,GAAU,SAAS,sBAAsB,KAAG,OAAO,CAAC,GAAG,GAAG,IAAI,CAsDzF,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,kBAAkB,GAC3B,SAAS,MAAM,EACf,MAAM,MAAM,EAAE,EACd,UAAS;IACL,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAA;IACtC,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAA;IACtC,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC1B,KACP,OAAO,CAAC,IAAI,CAUd,CAAA"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { execa } from 'execa';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
/**
|
|
4
|
+
* Execute a command with a spinner, handling different stdio configurations
|
|
5
|
+
* and optional data handlers for progress updates.
|
|
6
|
+
*/
|
|
7
|
+
export const execWithSpinner = async (options) => {
|
|
8
|
+
const { command, args, cwd, stdout = 'pipe', stderr = 'pipe', spinnerText = `Running ${command}...`, onDataHandle, onStderrHandle, checkExitCode = false, } = options;
|
|
9
|
+
const spinner = ora(spinnerText).start();
|
|
10
|
+
const subprocess = execa(command, args, {
|
|
11
|
+
cwd,
|
|
12
|
+
stdout,
|
|
13
|
+
stderr,
|
|
14
|
+
reject: false, // Don't reject on non-zero exit codes - we handle it manually
|
|
15
|
+
});
|
|
16
|
+
let stderrOutput = '';
|
|
17
|
+
await new Promise((res, rej) => {
|
|
18
|
+
if (onDataHandle) {
|
|
19
|
+
subprocess.stdout?.on('data', onDataHandle(spinner));
|
|
20
|
+
}
|
|
21
|
+
if (onStderrHandle) {
|
|
22
|
+
subprocess.stderr?.on('data', (data) => {
|
|
23
|
+
onStderrHandle(data);
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
else if (stderr === 'pipe') {
|
|
27
|
+
// Capture stderr to check for specific errors
|
|
28
|
+
subprocess.stderr?.on('data', (data) => {
|
|
29
|
+
stderrOutput += data.toString();
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
void subprocess.on('error', (e) => rej(e));
|
|
33
|
+
void subprocess.on('close', (code) => {
|
|
34
|
+
if (checkExitCode && code !== 0) {
|
|
35
|
+
const error = new Error(`${command} exited with code ${code}`);
|
|
36
|
+
if (stderrOutput) {
|
|
37
|
+
;
|
|
38
|
+
error.stderr = stderrOutput;
|
|
39
|
+
}
|
|
40
|
+
rej(error);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
res();
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
return spinner;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Execute a command without a spinner (for cases where we want direct output)
|
|
51
|
+
*/
|
|
52
|
+
export const execWithoutSpinner = async (command, args, options = {}) => {
|
|
53
|
+
const { cwd, stdout, stderr, reject = true, env } = options;
|
|
54
|
+
await execa(command, args, {
|
|
55
|
+
cwd,
|
|
56
|
+
stdout,
|
|
57
|
+
stderr,
|
|
58
|
+
reject,
|
|
59
|
+
env: env ? { ...process.env, ...env } : undefined,
|
|
60
|
+
});
|
|
61
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remove a package globally from all package managers (to ensure clean state)
|
|
3
|
+
* Silent - spinner shown but no output, no errors, no success messages
|
|
4
|
+
*/
|
|
5
|
+
export declare const removeGlobalPackage: (packageName: string) => Promise<void>;
|
|
6
|
+
/**
|
|
7
|
+
* Install a package globally using the detected package manager
|
|
8
|
+
*/
|
|
9
|
+
export declare const installGlobalPackage: (packageName: string, version?: string) => Promise<void>;
|
|
10
|
+
//# sourceMappingURL=global-package.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"global-package.d.ts","sourceRoot":"","sources":["../../src/lib/global-package.ts"],"names":[],"mappings":"AAyIA;;;GAGG;AACH,eAAO,MAAM,mBAAmB,GAAU,aAAa,MAAM,KAAG,OAAO,CAAC,IAAI,CAc3E,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,oBAAoB,GAAU,aAAa,MAAM,EAAE,UAAU,MAAM,KAAG,OAAO,CAAC,IAAI,CAsB9F,CAAA"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import { detectPackageManager } from './utils.js';
|
|
4
|
+
import { execWithSpinner, execWithoutSpinner } from './exec-utils.js';
|
|
5
|
+
import { log } from '@clack/prompts';
|
|
6
|
+
const getGlobalRemoveCommand = (pkgManager, packageName) => {
|
|
7
|
+
switch (pkgManager) {
|
|
8
|
+
case 'pnpm':
|
|
9
|
+
return ['pnpm', ['remove', '-g', packageName]];
|
|
10
|
+
case 'npm':
|
|
11
|
+
return ['npm', ['uninstall', '-g', packageName]];
|
|
12
|
+
case 'yarn':
|
|
13
|
+
return ['yarn', ['global', 'remove', packageName]];
|
|
14
|
+
case 'bun':
|
|
15
|
+
return ['bun', ['remove', '-g', packageName]];
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
const getGlobalInstallCommand = (pkgManager, packageName, version) => {
|
|
19
|
+
const packageSpec = version ? `${packageName}@${version}` : packageName;
|
|
20
|
+
switch (pkgManager) {
|
|
21
|
+
case 'pnpm':
|
|
22
|
+
return ['pnpm', ['add', '-g', packageSpec]];
|
|
23
|
+
case 'npm':
|
|
24
|
+
return ['npm', ['install', '-g', packageSpec]];
|
|
25
|
+
case 'yarn':
|
|
26
|
+
return ['yarn', ['global', 'add', packageSpec]];
|
|
27
|
+
case 'bun':
|
|
28
|
+
return ['bun', ['add', '-g', packageSpec]];
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const runGlobalRemoveCommand = async (pkgManager, packageName) => {
|
|
32
|
+
const [command, args] = getGlobalRemoveCommand(pkgManager, packageName);
|
|
33
|
+
// Silent execution with spinner - no output, no errors that quit
|
|
34
|
+
let stderrOutput = '';
|
|
35
|
+
let spinner = await execWithSpinner({
|
|
36
|
+
command,
|
|
37
|
+
args,
|
|
38
|
+
spinnerText: `Removing ${packageName} globally...`,
|
|
39
|
+
stdout: 'ignore',
|
|
40
|
+
stderr: 'pipe', // Capture stderr to detect path length errors
|
|
41
|
+
onStderrHandle: (data) => {
|
|
42
|
+
stderrOutput += data.toString();
|
|
43
|
+
},
|
|
44
|
+
checkExitCode: false, // Don't throw on errors - we want to silently ignore failures
|
|
45
|
+
});
|
|
46
|
+
// Check if we got the pnpm path length error
|
|
47
|
+
if (pkgManager === 'pnpm' && stderrOutput.toUpperCase().includes('PNPM_VIRTUAL_STORE_DIR_MAX_LENGTH_DIFF')) {
|
|
48
|
+
// Stop the current spinner before starting the fix
|
|
49
|
+
spinner?.stop();
|
|
50
|
+
// Run pnpm install -g to fix the issue (silently)
|
|
51
|
+
spinner = await execWithSpinner({
|
|
52
|
+
command: 'pnpm',
|
|
53
|
+
args: ['install', '-g', '--yes'],
|
|
54
|
+
stdout: 'ignore',
|
|
55
|
+
stderr: 'ignore',
|
|
56
|
+
checkExitCode: false,
|
|
57
|
+
});
|
|
58
|
+
spinner?.stop();
|
|
59
|
+
// Retry the remove command
|
|
60
|
+
spinner = await execWithSpinner({
|
|
61
|
+
command,
|
|
62
|
+
args,
|
|
63
|
+
spinnerText: `Removing ${packageName} globally...`,
|
|
64
|
+
stdout: 'ignore',
|
|
65
|
+
stderr: 'ignore',
|
|
66
|
+
checkExitCode: false,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
return spinner;
|
|
70
|
+
};
|
|
71
|
+
const runGlobalInstallCommand = async (pkgManager, packageName, version) => {
|
|
72
|
+
const [command, args] = getGlobalInstallCommand(pkgManager, packageName, version);
|
|
73
|
+
switch (pkgManager) {
|
|
74
|
+
// When using npm, inherit the stderr stream so that the progress bar is shown
|
|
75
|
+
case 'npm':
|
|
76
|
+
await execWithoutSpinner(command, args, {
|
|
77
|
+
stdout: 'inherit',
|
|
78
|
+
stderr: 'inherit',
|
|
79
|
+
});
|
|
80
|
+
return null;
|
|
81
|
+
// When using yarn or pnpm, use the stdout stream and ora spinner to show the progress
|
|
82
|
+
case 'pnpm':
|
|
83
|
+
return execWithSpinner({
|
|
84
|
+
command,
|
|
85
|
+
args,
|
|
86
|
+
spinnerText: `Installing ${packageName} globally...`,
|
|
87
|
+
onDataHandle: (spinner) => (data) => {
|
|
88
|
+
const text = data.toString();
|
|
89
|
+
if (text.includes('Progress')) {
|
|
90
|
+
spinner.text = text.includes('|') ? (text.split(' | ')[1] ?? '') : text;
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
case 'yarn':
|
|
95
|
+
return execWithSpinner({
|
|
96
|
+
command,
|
|
97
|
+
args,
|
|
98
|
+
spinnerText: `Installing ${packageName} globally...`,
|
|
99
|
+
onDataHandle: (spinner) => (data) => {
|
|
100
|
+
spinner.text = data.toString();
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
// When using bun, the stdout stream is ignored and the spinner is shown
|
|
104
|
+
case 'bun':
|
|
105
|
+
return execWithSpinner({
|
|
106
|
+
command,
|
|
107
|
+
args,
|
|
108
|
+
spinnerText: `Installing ${packageName} globally...`,
|
|
109
|
+
stdout: 'ignore',
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
/**
|
|
114
|
+
* Remove a package globally from all package managers (to ensure clean state)
|
|
115
|
+
* Silent - spinner shown but no output, no errors, no success messages
|
|
116
|
+
*/
|
|
117
|
+
export const removeGlobalPackage = async (packageName) => {
|
|
118
|
+
const allPMs = ['pnpm', 'npm', 'yarn', 'bun'];
|
|
119
|
+
for (const pm of allPMs) {
|
|
120
|
+
try {
|
|
121
|
+
const spinner = await runGlobalRemoveCommand(pm, packageName);
|
|
122
|
+
// Stop spinner silently without showing success/fail
|
|
123
|
+
if (spinner) {
|
|
124
|
+
spinner.stop();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
// Silently ignore all errors - package might not be installed with this PM
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* Install a package globally using the detected package manager
|
|
134
|
+
*/
|
|
135
|
+
export const installGlobalPackage = async (packageName, version) => {
|
|
136
|
+
log.info(chalk.cyan(`Installing ${packageName} globally...`));
|
|
137
|
+
const pkgManager = detectPackageManager();
|
|
138
|
+
try {
|
|
139
|
+
const installSpinner = await runGlobalInstallCommand(pkgManager, packageName, version);
|
|
140
|
+
// If the spinner was used to show the progress, use succeed method on it
|
|
141
|
+
// If not, create a new spinner for success message
|
|
142
|
+
if (installSpinner) {
|
|
143
|
+
installSpinner.stop();
|
|
144
|
+
// log.message(chalk.green(`Successfully installed ${packageName} globally!`))
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
// log.message(chalk.green(`Successfully installed ${packageName} globally!`))
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
// Stop any existing spinner and show failure
|
|
152
|
+
ora().fail(chalk.red(`Failed to install ${packageName} globally`));
|
|
153
|
+
log.warn(`⚠️ Could not install ${packageName} globally.`);
|
|
154
|
+
const [command, args] = getGlobalInstallCommand(pkgManager, packageName, version);
|
|
155
|
+
log.message(` - Please run ${chalk.gray(`${command} ${args.join(' ')}`)} manually.`);
|
|
156
|
+
throw error;
|
|
157
|
+
}
|
|
158
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install-deps.d.ts","sourceRoot":"","sources":["../../src/lib/install-deps.ts"],"names":[],"mappings":"AA4DA,eAAO,MAAM,mBAAmB,GAAU,gBAAgB;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,kBA6D/E,CAAA"}
|