create-nxpress-app 1.0.2 → 1.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.
Files changed (2) hide show
  1. package/dist/index.js +101 -80
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -12,27 +12,28 @@ const path_1 = __importDefault(require("path"));
12
12
  const child_process_1 = require("child_process");
13
13
  const program = new commander_1.Command();
14
14
  program
15
- .name('create-nxpress-app')
16
- .description('Scaffold a new Nxpress project')
17
- .argument('[project-directory]', 'Directory for the project')
18
- .option('-e, --engine <engine>', 'Template engine (handlebars, ejs, html)')
19
- .option('--tailwind', 'Include Tailwind CSS support', true)
20
- .option('--app-dir <dir>', 'Directory for application routes', 'app')
21
- .option('--components-dir <dir>', 'Directory for components', 'components')
22
- .option('--public-dir <dir>', 'Directory for static assets', 'public')
23
- .option('--skip-install', 'Skip installing dependencies', false)
15
+ .name("create-nxpress-app")
16
+ .description("Scaffold a new Nxpress project")
17
+ .argument("[project-directory]", "Directory for the project")
18
+ .option("-e, --engine <engine>", "Template engine (handlebars, ejs, html)")
19
+ .option("-p, --port <number>", "Port number")
20
+ .option("--tailwind", "Include Tailwind CSS support", true)
21
+ .option("--app-dir <dir>", "Directory for application routes")
22
+ .option("--components-dir <dir>", "Directory for components")
23
+ .option("--public-dir <dir>", "Directory for static assets")
24
+ .option("--skip-install", "Skip installing dependencies", false)
24
25
  .action(async (projectDirArg, options) => {
25
26
  console.log();
26
- (0, prompts_1.intro)(chalk_1.default.bgCyan.black(' create-nxpress-app '));
27
+ (0, prompts_1.intro)(chalk_1.default.bgCyan.black(" Create Nxpress App "));
27
28
  let projectDir = projectDirArg;
28
29
  if (!projectDir) {
29
30
  const res = await (0, prompts_1.text)({
30
- message: 'Where would you like to create your project?',
31
- placeholder: 'my-nxpress-app',
32
- defaultValue: 'my-nxpress-app',
31
+ message: "Where would you like to create your project?",
32
+ placeholder: "my-nxpress-app",
33
+ defaultValue: "my-nxpress-app",
33
34
  });
34
35
  if ((0, prompts_1.isCancel)(res)) {
35
- (0, prompts_1.cancel)('Operation cancelled.');
36
+ (0, prompts_1.cancel)("Operation cancelled.");
36
37
  process.exit(0);
37
38
  }
38
39
  projectDir = res;
@@ -44,66 +45,89 @@ program
44
45
  initialValue: false,
45
46
  });
46
47
  if ((0, prompts_1.isCancel)(overwrite) || !overwrite) {
47
- (0, prompts_1.cancel)('Operation cancelled.');
48
+ (0, prompts_1.cancel)("Operation cancelled.");
48
49
  process.exit(0);
49
50
  }
50
51
  }
51
52
  let engine = options.engine;
52
- if (!engine || !['handlebars', 'ejs', 'html'].includes(engine)) {
53
+ if (!engine || !["handlebars", "ejs", "html"].includes(engine)) {
53
54
  const selectedEngine = await (0, prompts_1.select)({
54
- message: 'Which template engine do you want to use?',
55
+ message: "Which template engine do you want to use?",
55
56
  options: [
56
- { value: 'handlebars', label: 'Handlebars (Recommended)', hint: 'Clean syntax & partials' },
57
- { value: 'ejs', label: 'EJS', hint: 'Embedded JavaScript templates' },
58
- { value: 'html', label: 'HTML', hint: 'Plain HTML templates' },
57
+ {
58
+ value: "handlebars",
59
+ label: "Handlebars (Recommended)",
60
+ hint: "Clean syntax & partials",
61
+ },
62
+ { value: "ejs", label: "EJS", hint: "Embedded JavaScript templates" },
63
+ { value: "html", label: "HTML", hint: "Plain HTML templates" },
59
64
  ],
60
65
  });
61
66
  if ((0, prompts_1.isCancel)(selectedEngine)) {
62
- (0, prompts_1.cancel)('Operation cancelled.');
67
+ (0, prompts_1.cancel)("Operation cancelled.");
63
68
  process.exit(0);
64
69
  }
65
70
  engine = selectedEngine;
66
71
  }
67
- let appDirName = options.appDir;
68
- let componentsDirName = options.componentsDir;
69
- let publicDirName = options.publicDir;
70
- if (process.stdin.isTTY && !options.appDir && !options.componentsDir && !options.publicDir) {
72
+ let port = options.port ? parseInt(options.port, 10) : 3000;
73
+ if (process.stdin.isTTY && !options.port) {
74
+ const portRes = await (0, prompts_1.text)({
75
+ message: "Port number:",
76
+ placeholder: "3000",
77
+ defaultValue: "3000",
78
+ validate: (val) => !!val && (isNaN(Number(val)) || Number(val) <= 0)
79
+ ? "Port must be a valid positive number"
80
+ : undefined,
81
+ });
82
+ if ((0, prompts_1.isCancel)(portRes)) {
83
+ (0, prompts_1.cancel)("Operation cancelled.");
84
+ process.exit(0);
85
+ }
86
+ port = parseInt(portRes, 10);
87
+ }
88
+ let appDirName = options.appDir || "app";
89
+ let componentsDirName = options.componentsDir || "components";
90
+ let publicDirName = options.publicDir || "public";
91
+ if (process.stdin.isTTY &&
92
+ !options.appDir &&
93
+ !options.componentsDir &&
94
+ !options.publicDir) {
71
95
  const customizeDirs = await (0, prompts_1.confirm)({
72
- message: 'Do you want to customize directory names?',
96
+ message: "Do you want to customize directory names?",
73
97
  initialValue: false,
74
98
  });
75
99
  if ((0, prompts_1.isCancel)(customizeDirs)) {
76
- (0, prompts_1.cancel)('Operation cancelled.');
100
+ (0, prompts_1.cancel)("Operation cancelled.");
77
101
  process.exit(0);
78
102
  }
79
103
  if (customizeDirs) {
80
104
  const appRes = await (0, prompts_1.text)({
81
- message: 'Routes directory name:',
82
- placeholder: 'app',
83
- defaultValue: 'app',
105
+ message: "Routes directory name:",
106
+ placeholder: "app",
107
+ defaultValue: "app",
84
108
  });
85
109
  if ((0, prompts_1.isCancel)(appRes)) {
86
- (0, prompts_1.cancel)('Operation cancelled.');
110
+ (0, prompts_1.cancel)("Operation cancelled.");
87
111
  process.exit(0);
88
112
  }
89
113
  appDirName = appRes;
90
114
  const compRes = await (0, prompts_1.text)({
91
- message: 'Components directory name:',
92
- placeholder: 'components',
93
- defaultValue: 'components',
115
+ message: "Components directory name:",
116
+ placeholder: "components",
117
+ defaultValue: "components",
94
118
  });
95
119
  if ((0, prompts_1.isCancel)(compRes)) {
96
- (0, prompts_1.cancel)('Operation cancelled.');
120
+ (0, prompts_1.cancel)("Operation cancelled.");
97
121
  process.exit(0);
98
122
  }
99
123
  componentsDirName = compRes;
100
124
  const pubRes = await (0, prompts_1.text)({
101
- message: 'Public directory name:',
102
- placeholder: 'public',
103
- defaultValue: 'public',
125
+ message: "Public directory name:",
126
+ placeholder: "public",
127
+ defaultValue: "public",
104
128
  });
105
129
  if ((0, prompts_1.isCancel)(pubRes)) {
106
- (0, prompts_1.cancel)('Operation cancelled.');
130
+ (0, prompts_1.cancel)("Operation cancelled.");
107
131
  process.exit(0);
108
132
  }
109
133
  publicDirName = pubRes;
@@ -112,17 +136,17 @@ program
112
136
  let shouldInstall = !options.skipInstall;
113
137
  if (!options.skipInstall && process.stdin.isTTY) {
114
138
  const res = await (0, prompts_1.confirm)({
115
- message: 'Install dependencies using pnpm?',
139
+ message: "Install dependencies using pnpm?",
116
140
  initialValue: true,
117
141
  });
118
142
  if ((0, prompts_1.isCancel)(res)) {
119
- (0, prompts_1.cancel)('Operation cancelled.');
143
+ (0, prompts_1.cancel)("Operation cancelled.");
120
144
  process.exit(0);
121
145
  }
122
146
  shouldInstall = res;
123
147
  }
124
148
  const s = (0, prompts_1.spinner)();
125
- s.start('Scaffolding project...');
149
+ s.start("Scaffolding project...");
126
150
  fs_extra_1.default.ensureDirSync(targetPath);
127
151
  // Create app structure with chosen directories
128
152
  const appDir = path_1.default.join(targetPath, appDirName);
@@ -131,9 +155,9 @@ program
131
155
  fs_extra_1.default.ensureDirSync(appDir);
132
156
  fs_extra_1.default.ensureDirSync(componentsDir);
133
157
  fs_extra_1.default.ensureDirSync(publicDir);
134
- const ext = engine === 'handlebars' ? 'hbs' : engine;
158
+ const ext = engine === "handlebars" ? "hbs" : engine;
135
159
  // Root layout
136
- const layoutContent = engine === 'handlebars'
160
+ const layoutContent = engine === "handlebars"
137
161
  ? `<!DOCTYPE html>
138
162
  <html lang="en">
139
163
  <head>
@@ -146,7 +170,7 @@ program
146
170
  {{{body}}}
147
171
  </body>
148
172
  </html>`
149
- : engine === 'ejs'
173
+ : engine === "ejs"
150
174
  ? `<!DOCTYPE html>
151
175
  <html lang="en">
152
176
  <head>
@@ -173,7 +197,7 @@ program
173
197
  </html>`;
174
198
  fs_extra_1.default.writeFileSync(path_1.default.join(appDir, `layout.${ext}`), layoutContent);
175
199
  // Index page
176
- const indexPageContent = engine === 'handlebars'
200
+ const indexPageContent = engine === "handlebars"
177
201
  ? `<div class="max-w-4xl mx-auto px-4 py-16 text-center">
178
202
  <h1 class="text-5xl font-bold mb-4 bg-gradient-to-r from-blue-400 to-indigo-500 bg-clip-text text-transparent">
179
203
  Welcome to {{title}}
@@ -183,7 +207,7 @@ program
183
207
  <p class="text-sm font-mono text-cyan-400">Edit <span class="text-amber-300">${appDirName}/index.${ext}</span> to get started.</p>
184
208
  </div>
185
209
  </div>`
186
- : engine === 'ejs'
210
+ : engine === "ejs"
187
211
  ? `<div class="max-w-4xl mx-auto px-4 py-16 text-center">
188
212
  <h1 class="text-5xl font-bold mb-4 bg-gradient-to-r from-blue-400 to-indigo-500 bg-clip-text text-transparent">
189
213
  Welcome to <%= title %>
@@ -201,7 +225,7 @@ program
201
225
  </div>`;
202
226
  fs_extra_1.default.writeFileSync(path_1.default.join(appDir, `index.${ext}`), indexPageContent);
203
227
  // Data loader for index page
204
- const indexTsContent = `import { Request, Response } from 'express';
228
+ const indexTsContent = `import type { Request, Response } from '@nxpress/core';
205
229
 
206
230
  export async function props(req: Request, res: Response) {
207
231
  return {
@@ -210,90 +234,87 @@ export async function props(req: Request, res: Response) {
210
234
  };
211
235
  }
212
236
  `;
213
- fs_extra_1.default.writeFileSync(path_1.default.join(appDir, 'index.ts'), indexTsContent);
237
+ fs_extra_1.default.writeFileSync(path_1.default.join(appDir, "index.ts"), indexTsContent);
214
238
  // App CSS for Tailwind v4
215
- fs_extra_1.default.writeFileSync(path_1.default.join(targetPath, 'app.css'), `@import "tailwindcss";\n`);
239
+ fs_extra_1.default.writeFileSync(path_1.default.join(targetPath, "app.css"), `@import "tailwindcss";\n`);
216
240
  // Server file
217
241
  const serverTsContent = `import { nxpress } from '@nxpress/core';
218
242
 
219
243
  const app = nxpress({
220
- engine: '${engine === 'handlebars' ? 'hbs' : engine}',
244
+ engine: '${engine === "handlebars" ? "hbs" : engine}',
221
245
  });
222
246
 
223
- const PORT = process.env.PORT || 3000;
247
+ const PORT = process.env.PORT || ${port};
224
248
  app.listen(PORT, () => {
225
249
  console.log(\`Nxpress server running on http://localhost:\${PORT}\`);
226
250
  });
227
251
  `;
228
- fs_extra_1.default.writeFileSync(path_1.default.join(targetPath, 'server.ts'), serverTsContent);
229
- const pkgVersion = '1.0.1';
252
+ fs_extra_1.default.writeFileSync(path_1.default.join(targetPath, "server.ts"), serverTsContent);
253
+ const pkgVersion = "1.0.4";
230
254
  // nxpress.config.json
231
255
  const nxConfig = {
232
256
  $schema: `https://unpkg.com/@nxpress/core@${pkgVersion}/schema.json`,
233
- port: 3000,
234
- engine: engine === 'handlebars' ? 'hbs' : engine,
257
+ port,
258
+ engine: engine === "handlebars" ? "hbs" : engine,
235
259
  appDir: appDirName,
236
260
  componentsDir: componentsDirName,
237
261
  publicDir: publicDirName,
238
262
  };
239
- fs_extra_1.default.writeFileSync(path_1.default.join(targetPath, 'nxpress.config.json'), JSON.stringify(nxConfig, null, 2));
263
+ fs_extra_1.default.writeFileSync(path_1.default.join(targetPath, "nxpress.config.json"), JSON.stringify(nxConfig, null, 2));
240
264
  // package.json for target project
241
265
  const projectPkgJson = {
242
266
  name: path_1.default.basename(targetPath),
243
- version: '0.1.0',
267
+ version: "0.1.0",
244
268
  private: true,
245
269
  scripts: {
246
- dev: 'nxpress dev',
247
- build: 'tsc',
248
- start: 'nxpress start',
270
+ dev: "nxpress dev",
271
+ build: "tsc",
272
+ start: "nxpress start",
249
273
  },
250
274
  dependencies: {
251
- '@nxpress/core': `^${pkgVersion}`,
252
- ...(engine === 'handlebars' ? { hbs: '^4.2.0' } : {}),
253
- ...(engine === 'ejs' ? { ejs: '^3.1.10' } : {}),
275
+ "@nxpress/core": `^${pkgVersion}`,
254
276
  },
255
277
  devDependencies: {
256
- typescript: '^5.3.3',
257
- '@types/node': '^20.11.24',
258
- '@types/express': '^4.17.21',
278
+ typescript: "^5.3.3",
279
+ "@types/node": "^20.11.24",
259
280
  },
260
281
  };
261
- fs_extra_1.default.writeFileSync(path_1.default.join(targetPath, 'package.json'), JSON.stringify(projectPkgJson, null, 2));
282
+ fs_extra_1.default.writeFileSync(path_1.default.join(targetPath, "package.json"), JSON.stringify(projectPkgJson, null, 2));
262
283
  // tsconfig.json for target project
263
284
  const projectTsConfig = {
264
285
  compilerOptions: {
265
- target: 'ES2022',
266
- module: 'CommonJS',
267
- moduleResolution: 'node',
286
+ target: "ES2022",
287
+ module: "CommonJS",
288
+ moduleResolution: "node",
268
289
  strict: true,
269
290
  esModuleInterop: true,
270
291
  skipLibCheck: true,
271
292
  },
272
- include: ['**/*'],
293
+ include: ["**/*"],
273
294
  };
274
- fs_extra_1.default.writeFileSync(path_1.default.join(targetPath, 'tsconfig.json'), JSON.stringify(projectTsConfig, null, 2));
295
+ fs_extra_1.default.writeFileSync(path_1.default.join(targetPath, "tsconfig.json"), JSON.stringify(projectTsConfig, null, 2));
275
296
  // .gitignore
276
297
  const gitignoreContent = `node_modules
277
298
  dist
278
299
  .env
279
300
  *.log
280
301
  `;
281
- fs_extra_1.default.writeFileSync(path_1.default.join(targetPath, '.gitignore'), gitignoreContent);
282
- s.stop('Project structure created successfully.');
302
+ fs_extra_1.default.writeFileSync(path_1.default.join(targetPath, ".gitignore"), gitignoreContent);
303
+ s.stop("Project structure created successfully.");
283
304
  if (shouldInstall) {
284
305
  const instSpinner = (0, prompts_1.spinner)();
285
- instSpinner.start('Installing dependencies with pnpm...');
306
+ instSpinner.start("Installing dependencies with pnpm...");
286
307
  try {
287
- (0, child_process_1.execSync)('pnpm install', { cwd: targetPath, stdio: 'ignore' });
288
- instSpinner.stop('Dependencies installed successfully.');
308
+ (0, child_process_1.execSync)("pnpm install", { cwd: targetPath, stdio: "ignore" });
309
+ instSpinner.stop("Dependencies installed successfully.");
289
310
  }
290
311
  catch (err) {
291
- instSpinner.stop('Failed to install dependencies automatically.');
312
+ instSpinner.stop("Failed to install dependencies automatically.");
292
313
  }
293
314
  }
294
315
  (0, prompts_1.outro)(`Project created in ${chalk_1.default.cyan(projectDir)}!\n\n` +
295
316
  `Next steps:\n` +
296
317
  ` ${chalk_1.default.cyan(`cd ${projectDir}`)}\n` +
297
- ` ${chalk_1.default.cyan('pnpm dev')}`);
318
+ ` ${chalk_1.default.cyan("pnpm dev")}`);
298
319
  });
299
320
  program.parse(process.argv);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-nxpress-app",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "CLI tool to create Nxpress applications",
5
5
  "main": "dist/index.js",
6
6
  "bin": {