create-next-rkk 2.0.2 → 2.0.3

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