create-nxpress-app 1.0.1
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 +2 -0
- package/dist/index.js +299 -0
- package/package.json +37 -0
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const prompts_1 = require("@clack/prompts");
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const commander_1 = require("commander");
|
|
10
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
11
|
+
const path_1 = __importDefault(require("path"));
|
|
12
|
+
const child_process_1 = require("child_process");
|
|
13
|
+
const program = new commander_1.Command();
|
|
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)
|
|
24
|
+
.action(async (projectDirArg, options) => {
|
|
25
|
+
console.log();
|
|
26
|
+
(0, prompts_1.intro)(chalk_1.default.bgCyan.black(' create-nxpress-app '));
|
|
27
|
+
let projectDir = projectDirArg;
|
|
28
|
+
if (!projectDir) {
|
|
29
|
+
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',
|
|
33
|
+
});
|
|
34
|
+
if ((0, prompts_1.isCancel)(res)) {
|
|
35
|
+
(0, prompts_1.cancel)('Operation cancelled.');
|
|
36
|
+
process.exit(0);
|
|
37
|
+
}
|
|
38
|
+
projectDir = res;
|
|
39
|
+
}
|
|
40
|
+
const targetPath = path_1.default.resolve(process.cwd(), projectDir);
|
|
41
|
+
if (fs_extra_1.default.existsSync(targetPath) && fs_extra_1.default.readdirSync(targetPath).length > 0) {
|
|
42
|
+
const overwrite = await (0, prompts_1.confirm)({
|
|
43
|
+
message: `Directory ${chalk_1.default.cyan(projectDir)} is not empty. Continue anyway?`,
|
|
44
|
+
initialValue: false,
|
|
45
|
+
});
|
|
46
|
+
if ((0, prompts_1.isCancel)(overwrite) || !overwrite) {
|
|
47
|
+
(0, prompts_1.cancel)('Operation cancelled.');
|
|
48
|
+
process.exit(0);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
let engine = options.engine;
|
|
52
|
+
if (!engine || !['handlebars', 'ejs', 'html'].includes(engine)) {
|
|
53
|
+
const selectedEngine = await (0, prompts_1.select)({
|
|
54
|
+
message: 'Which template engine do you want to use?',
|
|
55
|
+
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' },
|
|
59
|
+
],
|
|
60
|
+
});
|
|
61
|
+
if ((0, prompts_1.isCancel)(selectedEngine)) {
|
|
62
|
+
(0, prompts_1.cancel)('Operation cancelled.');
|
|
63
|
+
process.exit(0);
|
|
64
|
+
}
|
|
65
|
+
engine = selectedEngine;
|
|
66
|
+
}
|
|
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) {
|
|
71
|
+
const customizeDirs = await (0, prompts_1.confirm)({
|
|
72
|
+
message: 'Do you want to customize directory names?',
|
|
73
|
+
initialValue: false,
|
|
74
|
+
});
|
|
75
|
+
if ((0, prompts_1.isCancel)(customizeDirs)) {
|
|
76
|
+
(0, prompts_1.cancel)('Operation cancelled.');
|
|
77
|
+
process.exit(0);
|
|
78
|
+
}
|
|
79
|
+
if (customizeDirs) {
|
|
80
|
+
const appRes = await (0, prompts_1.text)({
|
|
81
|
+
message: 'Routes directory name:',
|
|
82
|
+
placeholder: 'app',
|
|
83
|
+
defaultValue: 'app',
|
|
84
|
+
});
|
|
85
|
+
if ((0, prompts_1.isCancel)(appRes)) {
|
|
86
|
+
(0, prompts_1.cancel)('Operation cancelled.');
|
|
87
|
+
process.exit(0);
|
|
88
|
+
}
|
|
89
|
+
appDirName = appRes;
|
|
90
|
+
const compRes = await (0, prompts_1.text)({
|
|
91
|
+
message: 'Components directory name:',
|
|
92
|
+
placeholder: 'components',
|
|
93
|
+
defaultValue: 'components',
|
|
94
|
+
});
|
|
95
|
+
if ((0, prompts_1.isCancel)(compRes)) {
|
|
96
|
+
(0, prompts_1.cancel)('Operation cancelled.');
|
|
97
|
+
process.exit(0);
|
|
98
|
+
}
|
|
99
|
+
componentsDirName = compRes;
|
|
100
|
+
const pubRes = await (0, prompts_1.text)({
|
|
101
|
+
message: 'Public directory name:',
|
|
102
|
+
placeholder: 'public',
|
|
103
|
+
defaultValue: 'public',
|
|
104
|
+
});
|
|
105
|
+
if ((0, prompts_1.isCancel)(pubRes)) {
|
|
106
|
+
(0, prompts_1.cancel)('Operation cancelled.');
|
|
107
|
+
process.exit(0);
|
|
108
|
+
}
|
|
109
|
+
publicDirName = pubRes;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
let shouldInstall = !options.skipInstall;
|
|
113
|
+
if (!options.skipInstall && process.stdin.isTTY) {
|
|
114
|
+
const res = await (0, prompts_1.confirm)({
|
|
115
|
+
message: 'Install dependencies using pnpm?',
|
|
116
|
+
initialValue: true,
|
|
117
|
+
});
|
|
118
|
+
if ((0, prompts_1.isCancel)(res)) {
|
|
119
|
+
(0, prompts_1.cancel)('Operation cancelled.');
|
|
120
|
+
process.exit(0);
|
|
121
|
+
}
|
|
122
|
+
shouldInstall = res;
|
|
123
|
+
}
|
|
124
|
+
const s = (0, prompts_1.spinner)();
|
|
125
|
+
s.start('Scaffolding project...');
|
|
126
|
+
fs_extra_1.default.ensureDirSync(targetPath);
|
|
127
|
+
// Create app structure with chosen directories
|
|
128
|
+
const appDir = path_1.default.join(targetPath, appDirName);
|
|
129
|
+
const componentsDir = path_1.default.join(targetPath, componentsDirName);
|
|
130
|
+
const publicDir = path_1.default.join(targetPath, publicDirName);
|
|
131
|
+
fs_extra_1.default.ensureDirSync(appDir);
|
|
132
|
+
fs_extra_1.default.ensureDirSync(componentsDir);
|
|
133
|
+
fs_extra_1.default.ensureDirSync(publicDir);
|
|
134
|
+
const ext = engine === 'handlebars' ? 'hbs' : engine;
|
|
135
|
+
// Root layout
|
|
136
|
+
const layoutContent = engine === 'handlebars'
|
|
137
|
+
? `<!DOCTYPE html>
|
|
138
|
+
<html lang="en">
|
|
139
|
+
<head>
|
|
140
|
+
<meta charset="UTF-8">
|
|
141
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
142
|
+
<title>{{title}}</title>
|
|
143
|
+
<link rel="stylesheet" href="/app.css">
|
|
144
|
+
</head>
|
|
145
|
+
<body class="bg-slate-900 text-white min-h-screen">
|
|
146
|
+
{{{body}}}
|
|
147
|
+
</body>
|
|
148
|
+
</html>`
|
|
149
|
+
: engine === 'ejs'
|
|
150
|
+
? `<!DOCTYPE html>
|
|
151
|
+
<html lang="en">
|
|
152
|
+
<head>
|
|
153
|
+
<meta charset="UTF-8">
|
|
154
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
155
|
+
<title><%= title %></title>
|
|
156
|
+
<link rel="stylesheet" href="/app.css">
|
|
157
|
+
</head>
|
|
158
|
+
<body class="bg-slate-900 text-white min-h-screen">
|
|
159
|
+
<%- body %>
|
|
160
|
+
</body>
|
|
161
|
+
</html>`
|
|
162
|
+
: `<!DOCTYPE html>
|
|
163
|
+
<html lang="en">
|
|
164
|
+
<head>
|
|
165
|
+
<meta charset="UTF-8">
|
|
166
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
167
|
+
<title>Nxpress App</title>
|
|
168
|
+
<link rel="stylesheet" href="/app.css">
|
|
169
|
+
</head>
|
|
170
|
+
<body class="bg-slate-900 text-white min-h-screen">
|
|
171
|
+
<!-- Content -->
|
|
172
|
+
</body>
|
|
173
|
+
</html>`;
|
|
174
|
+
fs_extra_1.default.writeFileSync(path_1.default.join(appDir, `layout.${ext}`), layoutContent);
|
|
175
|
+
// Index page
|
|
176
|
+
const indexPageContent = engine === 'handlebars'
|
|
177
|
+
? `<div class="max-w-4xl mx-auto px-4 py-16 text-center">
|
|
178
|
+
<h1 class="text-5xl font-bold mb-4 bg-gradient-to-r from-blue-400 to-indigo-500 bg-clip-text text-transparent">
|
|
179
|
+
Welcome to {{title}}
|
|
180
|
+
</h1>
|
|
181
|
+
<p class="text-xl text-slate-400 mb-8">{{description}}</p>
|
|
182
|
+
<div class="inline-block bg-slate-800 border border-slate-700 rounded-lg p-6 text-left">
|
|
183
|
+
<p class="text-sm font-mono text-cyan-400">Edit <span class="text-amber-300">${appDirName}/index.${ext}</span> to get started.</p>
|
|
184
|
+
</div>
|
|
185
|
+
</div>`
|
|
186
|
+
: engine === 'ejs'
|
|
187
|
+
? `<div class="max-w-4xl mx-auto px-4 py-16 text-center">
|
|
188
|
+
<h1 class="text-5xl font-bold mb-4 bg-gradient-to-r from-blue-400 to-indigo-500 bg-clip-text text-transparent">
|
|
189
|
+
Welcome to <%= title %>
|
|
190
|
+
</h1>
|
|
191
|
+
<p class="text-xl text-slate-400 mb-8"><%= description %></p>
|
|
192
|
+
<div class="inline-block bg-slate-800 border border-slate-700 rounded-lg p-6 text-left">
|
|
193
|
+
<p class="text-sm font-mono text-cyan-400">Edit <span class="text-amber-300">${appDirName}/index.${ext}</span> to get started.</p>
|
|
194
|
+
</div>
|
|
195
|
+
</div>`
|
|
196
|
+
: `<div class="max-w-4xl mx-auto px-4 py-16 text-center">
|
|
197
|
+
<h1 class="text-5xl font-bold mb-4 bg-gradient-to-r from-blue-400 to-indigo-500 bg-clip-text text-transparent">
|
|
198
|
+
Welcome to Nxpress
|
|
199
|
+
</h1>
|
|
200
|
+
<p class="text-xl text-slate-400 mb-8">Express.js with Next.js developer experience</p>
|
|
201
|
+
</div>`;
|
|
202
|
+
fs_extra_1.default.writeFileSync(path_1.default.join(appDir, `index.${ext}`), indexPageContent);
|
|
203
|
+
// Data loader for index page
|
|
204
|
+
const indexTsContent = `import { Request, Response } from 'express';
|
|
205
|
+
|
|
206
|
+
export async function props(req: Request, res: Response) {
|
|
207
|
+
return {
|
|
208
|
+
title: 'Nxpress App',
|
|
209
|
+
description: 'Express.js with Next.js developer experience',
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
`;
|
|
213
|
+
fs_extra_1.default.writeFileSync(path_1.default.join(appDir, 'index.ts'), indexTsContent);
|
|
214
|
+
// App CSS for Tailwind v4
|
|
215
|
+
fs_extra_1.default.writeFileSync(path_1.default.join(targetPath, 'app.css'), `@import "tailwindcss";\n`);
|
|
216
|
+
// Server file
|
|
217
|
+
const serverTsContent = `import { nxpress } from '@nxpress/core';
|
|
218
|
+
|
|
219
|
+
const app = nxpress({
|
|
220
|
+
engine: '${engine === 'handlebars' ? 'hbs' : engine}',
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
const PORT = process.env.PORT || 3000;
|
|
224
|
+
app.listen(PORT, () => {
|
|
225
|
+
console.log(\`Nxpress server running on http://localhost:\${PORT}\`);
|
|
226
|
+
});
|
|
227
|
+
`;
|
|
228
|
+
fs_extra_1.default.writeFileSync(path_1.default.join(targetPath, 'server.ts'), serverTsContent);
|
|
229
|
+
const pkgVersion = '1.0.1';
|
|
230
|
+
// nxpress.config.json
|
|
231
|
+
const nxConfig = {
|
|
232
|
+
$schema: `https://unpkg.com/@nxpress/core@${pkgVersion}/schema.json`,
|
|
233
|
+
port: 3000,
|
|
234
|
+
engine: engine === 'handlebars' ? 'hbs' : engine,
|
|
235
|
+
appDir: appDirName,
|
|
236
|
+
componentsDir: componentsDirName,
|
|
237
|
+
publicDir: publicDirName,
|
|
238
|
+
};
|
|
239
|
+
fs_extra_1.default.writeFileSync(path_1.default.join(targetPath, 'nxpress.config.json'), JSON.stringify(nxConfig, null, 2));
|
|
240
|
+
// package.json for target project
|
|
241
|
+
const projectPkgJson = {
|
|
242
|
+
name: path_1.default.basename(targetPath),
|
|
243
|
+
version: '0.1.0',
|
|
244
|
+
private: true,
|
|
245
|
+
scripts: {
|
|
246
|
+
dev: 'nxpress dev',
|
|
247
|
+
build: 'tsc',
|
|
248
|
+
start: 'nxpress start',
|
|
249
|
+
},
|
|
250
|
+
dependencies: {
|
|
251
|
+
'@nxpress/core': `^${pkgVersion}`,
|
|
252
|
+
...(engine === 'handlebars' ? { hbs: '^4.2.0' } : {}),
|
|
253
|
+
...(engine === 'ejs' ? { ejs: '^3.1.10' } : {}),
|
|
254
|
+
},
|
|
255
|
+
devDependencies: {
|
|
256
|
+
typescript: '^5.3.3',
|
|
257
|
+
'@types/node': '^20.11.24',
|
|
258
|
+
'@types/express': '^4.17.21',
|
|
259
|
+
},
|
|
260
|
+
};
|
|
261
|
+
fs_extra_1.default.writeFileSync(path_1.default.join(targetPath, 'package.json'), JSON.stringify(projectPkgJson, null, 2));
|
|
262
|
+
// tsconfig.json for target project
|
|
263
|
+
const projectTsConfig = {
|
|
264
|
+
compilerOptions: {
|
|
265
|
+
target: 'ES2022',
|
|
266
|
+
module: 'CommonJS',
|
|
267
|
+
moduleResolution: 'node',
|
|
268
|
+
strict: true,
|
|
269
|
+
esModuleInterop: true,
|
|
270
|
+
skipLibCheck: true,
|
|
271
|
+
},
|
|
272
|
+
include: ['**/*'],
|
|
273
|
+
};
|
|
274
|
+
fs_extra_1.default.writeFileSync(path_1.default.join(targetPath, 'tsconfig.json'), JSON.stringify(projectTsConfig, null, 2));
|
|
275
|
+
// .gitignore
|
|
276
|
+
const gitignoreContent = `node_modules
|
|
277
|
+
dist
|
|
278
|
+
.env
|
|
279
|
+
*.log
|
|
280
|
+
`;
|
|
281
|
+
fs_extra_1.default.writeFileSync(path_1.default.join(targetPath, '.gitignore'), gitignoreContent);
|
|
282
|
+
s.stop('Project structure created successfully.');
|
|
283
|
+
if (shouldInstall) {
|
|
284
|
+
const instSpinner = (0, prompts_1.spinner)();
|
|
285
|
+
instSpinner.start('Installing dependencies with pnpm...');
|
|
286
|
+
try {
|
|
287
|
+
(0, child_process_1.execSync)('pnpm install', { cwd: targetPath, stdio: 'ignore' });
|
|
288
|
+
instSpinner.stop('Dependencies installed successfully.');
|
|
289
|
+
}
|
|
290
|
+
catch (err) {
|
|
291
|
+
instSpinner.stop('Failed to install dependencies automatically.');
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
(0, prompts_1.outro)(`Project created in ${chalk_1.default.cyan(projectDir)}!\n\n` +
|
|
295
|
+
`Next steps:\n` +
|
|
296
|
+
` ${chalk_1.default.cyan(`cd ${projectDir}`)}\n` +
|
|
297
|
+
` ${chalk_1.default.cyan('pnpm dev')}`);
|
|
298
|
+
});
|
|
299
|
+
program.parse(process.argv);
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-nxpress-app",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "CLI tool to create Nxpress applications",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-nxpress-app": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"templates"
|
|
12
|
+
],
|
|
13
|
+
"keywords": [
|
|
14
|
+
"nxpress",
|
|
15
|
+
"cli",
|
|
16
|
+
"create-app",
|
|
17
|
+
"scaffold",
|
|
18
|
+
"express"
|
|
19
|
+
],
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@clack/prompts": "^0.8.2",
|
|
23
|
+
"chalk": "^4.1.2",
|
|
24
|
+
"commander": "^12.0.0",
|
|
25
|
+
"fs-extra": "^11.2.0"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/fs-extra": "^11.0.4",
|
|
29
|
+
"@types/node": "^20.11.24",
|
|
30
|
+
"typescript": "^5.3.3"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "tsc",
|
|
34
|
+
"dev": "tsc --watch",
|
|
35
|
+
"start": "node dist/index.js"
|
|
36
|
+
}
|
|
37
|
+
}
|