create-next-rkk 2.0.2 → 2.0.4

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.d.ts CHANGED
@@ -4,4 +4,21 @@ export declare function validateProjectName(name: string): {
4
4
  reason?: string;
5
5
  };
6
6
  export declare function cleanupProjectDir(projectPath: string): void;
7
+ /**
8
+ * Detect which package manager is available, preferring pnpm → yarn → npm.
9
+ * Returns the binary name ('pnpm' | 'yarn' | 'npm').
10
+ */
11
+ export declare function detectPackageManager(): 'pnpm' | 'yarn' | 'npm';
12
+ /**
13
+ * Build the `create-next-app` scaffold command for a given package manager.
14
+ * Matches the exact defaults that `pnpm create next-app@latest --yes` uses:
15
+ * - TypeScript
16
+ * - ESLint
17
+ * - No Tailwind
18
+ * - NO --src-dir (app/ lives at project root)
19
+ * - App Router or Pages Router based on user choice
20
+ * - import alias @/*
21
+ * - --yes skips all interactive prompts
22
+ */
23
+ export declare function buildCreateCommand(pm: 'pnpm' | 'yarn' | 'npm', targetDir: string, typescript: boolean, router: 'app' | 'pages'): string;
7
24
  export declare function runCli(argv?: string[]): Promise<void>;
package/dist/index.js CHANGED
@@ -39,6 +39,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
39
39
  Object.defineProperty(exports, "__esModule", { value: true });
40
40
  exports.validateProjectName = validateProjectName;
41
41
  exports.cleanupProjectDir = cleanupProjectDir;
42
+ exports.detectPackageManager = detectPackageManager;
43
+ exports.buildCreateCommand = buildCreateCommand;
42
44
  exports.runCli = runCli;
43
45
  const commander_1 = require("commander");
44
46
  const inquirer_1 = __importDefault(require("inquirer"));
@@ -69,10 +71,95 @@ function cleanupProjectDir(projectPath) {
69
71
  fs.rmSync(projectPath, { recursive: true, force: true });
70
72
  }
71
73
  }
74
+ /**
75
+ * Detect which package manager is available, preferring pnpm → yarn → npm.
76
+ * Returns the binary name ('pnpm' | 'yarn' | 'npm').
77
+ */
78
+ function detectPackageManager() {
79
+ for (const pm of ['pnpm', 'yarn']) {
80
+ try {
81
+ (0, child_process_1.execSync)(`${pm} --version`, { stdio: 'ignore' });
82
+ return pm;
83
+ }
84
+ catch {
85
+ // not available
86
+ }
87
+ }
88
+ return 'npm';
89
+ }
90
+ /**
91
+ * Build the `create-next-app` scaffold command for a given package manager.
92
+ * Matches the exact defaults that `pnpm create next-app@latest --yes` uses:
93
+ * - TypeScript
94
+ * - ESLint
95
+ * - No Tailwind
96
+ * - NO --src-dir (app/ lives at project root)
97
+ * - App Router or Pages Router based on user choice
98
+ * - import alias @/*
99
+ * - --yes skips all interactive prompts
100
+ */
101
+ function buildCreateCommand(pm, targetDir, typescript, router) {
102
+ const tsFlag = typescript ? '--typescript' : '--js';
103
+ const routerFlag = router === 'app' ? '--app' : '--no-app';
104
+ if (pm === 'pnpm') {
105
+ return [
106
+ 'pnpm',
107
+ 'create',
108
+ 'next-app@latest',
109
+ targetDir,
110
+ '--yes',
111
+ tsFlag,
112
+ '--eslint',
113
+ '--no-tailwind',
114
+ routerFlag,
115
+ '--no-src-dir',
116
+ '--import-alias', '@/*',
117
+ ].join(' ');
118
+ }
119
+ if (pm === 'yarn') {
120
+ return [
121
+ 'yarn',
122
+ 'create',
123
+ 'next-app',
124
+ targetDir,
125
+ tsFlag,
126
+ '--eslint',
127
+ '--no-tailwind',
128
+ routerFlag,
129
+ '--no-src-dir',
130
+ '--import-alias', '@/*',
131
+ '--yes',
132
+ ].join(' ');
133
+ }
134
+ // npm / npx
135
+ return [
136
+ 'npx',
137
+ '--yes',
138
+ 'create-next-app@latest',
139
+ targetDir,
140
+ '--yes',
141
+ tsFlag,
142
+ '--eslint',
143
+ '--no-tailwind',
144
+ routerFlag,
145
+ '--no-src-dir',
146
+ '--import-alias', '@/*',
147
+ ].join(' ');
148
+ }
149
+ /**
150
+ * Build the install command for rkk-next.
151
+ */
152
+ function buildInstallCommand(pm) {
153
+ if (pm === 'pnpm')
154
+ return 'pnpm add rkk-next';
155
+ if (pm === 'yarn')
156
+ return 'yarn add rkk-next';
157
+ return 'npm install rkk-next --legacy-peer-deps';
158
+ }
72
159
  program
73
160
  .name('create-next-rkk')
74
161
  .description('Create a new Next.js app with rkk-next pre-configured')
75
- .version('2.0.2')
162
+ .version('2.0.4')
76
163
  .argument('[project-name]', 'name of your project')
77
164
  .action(async (projectName) => {
78
165
  console.log(chalk_1.default.bold.cyan('\n🚀 Create RKK Next.js App\n'));
@@ -113,63 +200,48 @@ program
113
200
  console.error(chalk_1.default.red(`\nInvalid project name: ${validation.reason}\n`));
114
201
  process.exit(1);
115
202
  }
203
+ const pm = detectPackageManager();
116
204
  const projectPath = path.join(process.cwd(), targetDir);
117
205
  let createdProject = false;
118
206
  let activeStep = null;
119
- // Step 1: Create Next.js app
120
207
  const spinner = (0, ora_1.default)('Creating Next.js application...').start();
121
208
  try {
122
- // Use create-next-app with all options specified to avoid prompts.
123
- // --yes tells npx to auto-install create-next-app without asking.
124
- const createNextAppCmd = [
125
- 'npx',
126
- '--yes',
127
- 'create-next-app@latest',
128
- targetDir,
129
- answers.typescript ? '--typescript' : '--js',
130
- '--eslint',
131
- '--no-tailwind',
132
- '--src-dir',
133
- answers.router === 'app' ? '--app' : '--no-app',
134
- '--import-alias', '@/*',
135
- '--no-git',
136
- ].join(' ');
209
+ // ── Step 1: Scaffold with create-next-app ──────────────────────────────
210
+ const createCmd = buildCreateCommand(pm, targetDir, answers.typescript, answers.router);
137
211
  activeStep = 'create-app';
138
- (0, child_process_1.execSync)(createNextAppCmd, {
212
+ (0, child_process_1.execSync)(createCmd, {
139
213
  stdio: 'inherit',
140
- cwd: process.cwd()
214
+ cwd: process.cwd(),
141
215
  });
142
216
  spinner.succeed('Next.js application created!');
143
217
  createdProject = true;
144
- // Verify directory exists
145
218
  if (!fs.existsSync(projectPath)) {
146
- throw new Error(`Project directory ${targetDir} was not created`);
219
+ throw new Error(`Project directory "${targetDir}" was not created`);
147
220
  }
148
- // Step 2: Install rkk-next
221
+ // ── Step 2: Install rkk-next ───────────────────────────────────────────
149
222
  if (answers.installDeps) {
150
223
  activeStep = 'install-rkk';
151
- spinner.start('Installing rkk-next...');
152
- (0, child_process_1.execSync)('npm install rkk-next --legacy-peer-deps', {
224
+ spinner.start(`Installing rkk-next (via ${pm})...`);
225
+ (0, child_process_1.execSync)(buildInstallCommand(pm), {
153
226
  stdio: 'inherit',
154
- cwd: projectPath
227
+ cwd: projectPath,
155
228
  });
156
229
  spinner.succeed('rkk-next installed!');
157
230
  }
158
- // Step 3: Setup template files
231
+ // ── Step 3: Inject rkk-next into generated files ───────────────────────
159
232
  activeStep = 'setup-templates';
160
233
  spinner.start('Setting up rkk-next configuration...');
161
234
  setupTemplateFiles(projectPath, answers.router, answers.typescript);
162
235
  spinner.succeed('Configuration complete!');
163
- // Success message
236
+ // ── Done ───────────────────────────────────────────────────────────────
164
237
  console.log(chalk_1.default.green.bold('\n✨ Success! Created ' + targetDir));
165
- console.log(chalk_1.default.cyan('\n📁 Inside your project directory:\n'));
238
+ console.log(chalk_1.default.cyan('\nGet started:\n'));
166
239
  console.log(chalk_1.default.white(' cd ' + targetDir));
167
240
  if (!answers.installDeps) {
168
- console.log(chalk_1.default.white(' npm install'));
241
+ console.log(chalk_1.default.white(` ${pm} install`));
169
242
  }
170
- console.log(chalk_1.default.white(' npm run dev'));
171
- console.log(chalk_1.default.cyan('\n📚 Documentation:'));
172
- console.log(chalk_1.default.white(' https://github.com/ROHIT8759/rkk-next\n'));
243
+ console.log(chalk_1.default.white(` ${pm === 'npm' ? 'npm run' : pm} dev`));
244
+ console.log(chalk_1.default.cyan('\n📚 Docs: https://github.com/ROHIT8759/rkk-next\n'));
173
245
  console.log(chalk_1.default.yellow('Happy coding! 🎉\n'));
174
246
  }
175
247
  catch (error) {
@@ -189,112 +261,222 @@ program
189
261
  process.exit(1);
190
262
  }
191
263
  });
264
+ /**
265
+ * Inject rkk-next features into the scaffolded project.
266
+ *
267
+ * Final structure (App Router, matches pnpm create next-app@latest):
268
+ *
269
+ * app/
270
+ * layout.tsx
271
+ * page.tsx
272
+ * globals.css (kept from scaffold)
273
+ * api/
274
+ * health/
275
+ * route.ts ← simple health-check
276
+ * hello/
277
+ * route.ts ← demo JSON endpoint with rkk middleware
278
+ * public/ (kept from scaffold)
279
+ * next.config.ts
280
+ * tsconfig.json (kept from scaffold)
281
+ * package.json (kept from scaffold)
282
+ * eslint.config.mjs (kept from scaffold)
283
+ * postcss.config.mjs (kept from scaffold)
284
+ * pnpm-lock.yaml (kept from scaffold)
285
+ */
192
286
  function setupTemplateFiles(projectDir, router, typescript) {
193
287
  const ext = typescript ? 'tsx' : 'jsx';
194
- const srcDir = path.join(projectDir, 'src');
288
+ const routeExt = typescript ? 'ts' : 'js';
195
289
  if (router === 'pages') {
196
- // Setup Pages Router
197
- const pagesDir = path.join(srcDir, 'pages');
198
- // _app file
199
- const appContent = `import type { AppProps } from 'next/app';
200
- import { reportWebVitals } from 'rkk-next';
201
- import '../styles/globals.css';
202
-
203
- export default function App({ Component, pageProps }: AppProps) {
204
- return <Component {...pageProps} />;
205
- }
206
-
207
- export { reportWebVitals };
208
- `;
209
- // index.tsx with rkk-next
210
- const indexContent = `import { MetaManager, SmartLink, OptimizedImage } from 'rkk-next';
211
-
212
- export default function Home() {
213
- return (
214
- <>
215
- <MetaManager
216
- title="Home | My App"
217
- description="Built with rkk-next and Next.js"
218
- keywords="nextjs, seo, performance"
219
- />
220
-
221
- <main style={{ padding: '2rem', maxWidth: '1200px', margin: '0 auto' }}>
222
- <h1>Welcome to Your RKK Next.js App!</h1>
223
- <p>This app is pre-configured with rkk-next for SEO, performance, and routing optimization.</p>
224
-
225
- <SmartLink href="/about">
226
- Learn More
227
- </SmartLink>
228
- </main>
229
- </>
230
- );
231
- }
232
- `;
233
- fs.writeFileSync(path.join(pagesDir, `_app.${ext}`), appContent);
234
- fs.writeFileSync(path.join(pagesDir, `index.${ext}`), indexContent);
290
+ // ── Pages Router ────────────────────────────────────────────────────────
291
+ // Structure:
292
+ // pages/
293
+ // _app.tsx
294
+ // index.tsx
295
+ // api/
296
+ // health.ts
297
+ // hello.ts
298
+ const pagesDir = path.join(projectDir, 'pages');
299
+ const pagesApiDir = path.join(pagesDir, 'api');
300
+ fs.mkdirSync(pagesApiDir, { recursive: true });
301
+ // pages/_app.tsx
302
+ fs.writeFileSync(path.join(pagesDir, `_app.${ext}`), `import type { AppProps } from 'next/app';
303
+ import { reportWebVitals } from 'rkk-next';
304
+ import '../styles/globals.css';
305
+
306
+ export default function App({ Component, pageProps }: AppProps) {
307
+ return <Component {...pageProps} />;
308
+ }
309
+
310
+ export { reportWebVitals };
311
+ `);
312
+ // pages/index.tsx
313
+ fs.writeFileSync(path.join(pagesDir, `index.${ext}`), `import { MetaManager, SmartLink } from 'rkk-next';
314
+
315
+ export default function Home() {
316
+ return (
317
+ <>
318
+ <MetaManager
319
+ title="Home | My App"
320
+ description="Built with rkk-next and Next.js"
321
+ keywords="nextjs, seo, performance"
322
+ />
323
+ <main style={{ padding: '2rem', maxWidth: '1200px', margin: '0 auto' }}>
324
+ <h1>Welcome to Your RKK Next.js App!</h1>
325
+ <p>Pre-configured with rkk-next for SEO, performance, and routing.</p>
326
+ <SmartLink href="/about">Learn More →</SmartLink>
327
+ </main>
328
+ </>
329
+ );
330
+ }
331
+ `);
332
+ // pages/api/health.ts
333
+ fs.writeFileSync(path.join(pagesApiDir, `health.${routeExt}`), `import type { NextApiRequest, NextApiResponse } from 'next';
334
+ import { composeMiddleware, cors, logger } from 'rkk-next';
335
+
336
+ function handler(_req: NextApiRequest, res: NextApiResponse) {
337
+ res.status(200).json({ status: 'ok', timestamp: new Date().toISOString() });
338
+ }
339
+
340
+ export default composeMiddleware(
341
+ cors(),
342
+ logger()
343
+ )(handler);
344
+ `);
345
+ // pages/api/hello.ts
346
+ fs.writeFileSync(path.join(pagesApiDir, `hello.${routeExt}`), `import type { NextApiRequest, NextApiResponse } from 'next';
347
+ import { composeMiddleware, cors, rateLimit, logger, jsonResponse } from 'rkk-next';
348
+
349
+ function handler(req: NextApiRequest, res: NextApiResponse) {
350
+ jsonResponse(res, 200, { message: 'Hello from rkk-next!', method: req.method });
351
+ }
352
+
353
+ export default composeMiddleware(
354
+ cors(),
355
+ rateLimit({ windowMs: 60_000, max: 60 }),
356
+ logger()
357
+ )(handler);
358
+ `);
235
359
  }
236
360
  else {
237
- // Setup App Router
238
- const appDir = path.join(srcDir, 'app');
239
- // layout.tsx
240
- const layoutContent = `import { generateAppMetadata } from 'rkk-next';
241
- import './globals.css';
242
-
243
- export const metadata = generateAppMetadata({
244
- title: 'My RKK App',
245
- description: 'Built with rkk-next and Next.js',
246
- image: '/og-image.png',
247
- });
248
-
249
- export default function RootLayout({
250
- children,
251
- }: {
252
- children: React.ReactNode;
253
- }) {
254
- return (
255
- <html lang="en">
256
- <body>{children}</body>
257
- </html>
258
- );
259
- }
260
- `;
261
- // page.tsx
262
- const pageContent = `import { JsonLd, OptimizedImage } from 'rkk-next';
263
- import Link from 'next/link';
264
-
265
- export default function HomePage() {
266
- return (
267
- <>
268
- <JsonLd
269
- type="WebSite"
270
- data={{
271
- name: "My RKK App",
272
- url: "https://myapp.com",
273
- }}
274
- />
275
-
276
- <main style={{ padding: '2rem', maxWidth: '1200px', margin: '0 auto' }}>
277
- <h1>Welcome to Your RKK Next.js App!</h1>
278
- <p>This app is pre-configured with rkk-next for SEO, performance, and routing optimization.</p>
279
-
280
- <Link href="/about">Learn More →</Link>
281
- </main>
282
- </>
283
- );
284
- }
285
- `;
286
- fs.writeFileSync(path.join(appDir, `layout.${ext}`), layoutContent);
287
- fs.writeFileSync(path.join(appDir, `page.${ext}`), pageContent);
361
+ // ── App Router ──────────────────────────────────────────────────────────
362
+ // Structure (app/ at project root — NO src/):
363
+ // app/
364
+ // layout.tsx
365
+ // page.tsx
366
+ // globals.css (kept from scaffold)
367
+ // api/
368
+ // health/
369
+ // route.ts
370
+ // hello/
371
+ // route.ts
372
+ const appDir = path.join(projectDir, 'app');
373
+ const apiDir = path.join(appDir, 'api');
374
+ const healthDir = path.join(apiDir, 'health');
375
+ const helloDir = path.join(apiDir, 'hello');
376
+ fs.mkdirSync(healthDir, { recursive: true });
377
+ fs.mkdirSync(helloDir, { recursive: true });
378
+ // app/layout.tsx
379
+ fs.writeFileSync(path.join(appDir, `layout.${ext}`), `import type { Metadata } from 'next';
380
+ import { generateAppMetadata } from 'rkk-next';
381
+ import './globals.css';
382
+
383
+ export const metadata: Metadata = generateAppMetadata({
384
+ title: 'My RKK App',
385
+ description: 'Built with rkk-next and Next.js',
386
+ image: '/og-image.png',
387
+ });
388
+
389
+ export default function RootLayout({
390
+ children,
391
+ }: {
392
+ children: React.ReactNode;
393
+ }) {
394
+ return (
395
+ <html lang="en">
396
+ <body>{children}</body>
397
+ </html>
398
+ );
399
+ }
400
+ `);
401
+ // app/page.tsx
402
+ fs.writeFileSync(path.join(appDir, `page.${ext}`), `import { JsonLd } from 'rkk-next';
403
+ import Link from 'next/link';
404
+
405
+ export default function HomePage() {
406
+ return (
407
+ <>
408
+ <JsonLd
409
+ type="WebSite"
410
+ data={{
411
+ name: 'My RKK App',
412
+ url: 'https://myapp.com',
413
+ }}
414
+ />
415
+ <main style={{ padding: '2rem', maxWidth: '1200px', margin: '0 auto' }}>
416
+ <h1>Welcome to Your RKK Next.js App!</h1>
417
+ <p>Pre-configured with rkk-next for SEO, performance, and routing.</p>
418
+ <Link href="/about">Learn More →</Link>
419
+ </main>
420
+ </>
421
+ );
422
+ }
423
+ `);
424
+ // app/api/health/route.ts — GET /api/health
425
+ fs.writeFileSync(path.join(healthDir, `route.${routeExt}`), `import { NextResponse } from 'next/server';
426
+
427
+ export const dynamic = 'force-dynamic';
428
+
429
+ export function GET() {
430
+ return NextResponse.json(
431
+ { status: 'ok', timestamp: new Date().toISOString() },
432
+ { status: 200 }
433
+ );
434
+ }
435
+ `);
436
+ // app/api/hello/route.ts — GET + POST /api/hello (with rkk-next middleware)
437
+ fs.writeFileSync(path.join(helloDir, `route.${routeExt}`), `import { NextRequest, NextResponse } from 'next/server';
438
+ import { cacheHeaders, SHORT_TERM_CACHE } from 'rkk-next';
439
+
440
+ export const dynamic = 'force-dynamic';
441
+
442
+ export function GET(req: NextRequest) {
443
+ const { searchParams } = new URL(req.url);
444
+ const name = searchParams.get('name') ?? 'World';
445
+
446
+ return NextResponse.json(
447
+ { message: \`Hello, \${name}! Powered by rkk-next.\` },
448
+ {
449
+ status: 200,
450
+ headers: {
451
+ // Apply rkk-next short-term cache headers
452
+ 'Cache-Control': SHORT_TERM_CACHE[0].value,
453
+ },
288
454
  }
289
- // Create next.config.js with rkk-next
290
- const configContent = `/** @type {import('next').NextConfig} */
291
- const nextConfig = {
292
- reactStrictMode: true,
293
- };
294
-
295
- module.exports = nextConfig;
296
- `;
297
- fs.writeFileSync(path.join(projectDir, 'next.config.js'), configContent);
455
+ );
456
+ }
457
+
458
+ export async function POST(req: NextRequest) {
459
+ const body = await req.json().catch(() => ({}));
460
+ return NextResponse.json(
461
+ { received: body, timestamp: new Date().toISOString() },
462
+ { status: 201 }
463
+ );
464
+ }
465
+ `);
466
+ }
467
+ // ── next.config.ts (TypeScript, matches pnpm scaffold) ───────────────────
468
+ const configTsPath = path.join(projectDir, 'next.config.ts');
469
+ const configJsPath = path.join(projectDir, 'next.config.js');
470
+ fs.writeFileSync(configTsPath, `import type { NextConfig } from 'next';
471
+
472
+ const nextConfig: NextConfig = {
473
+ reactStrictMode: true,
474
+ };
475
+
476
+ export default nextConfig;
477
+ `);
478
+ if (fs.existsSync(configJsPath))
479
+ fs.unlinkSync(configJsPath);
298
480
  }
299
481
  async function runCli(argv = process.argv) {
300
482
  await program.parseAsync(argv);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-next-rkk",
3
- "version": "2.0.2",
3
+ "version": "2.0.4",
4
4
  "description": "CLI tool to create Next.js apps with rkk-next pre-configured",
5
5
  "author": "Rohit Kumar Kundu",
6
6
  "license": "MIT",