nibula 1.6.0 → 1.6.2

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 (99) hide show
  1. package/.eleventy.js +82 -81
  2. package/CHANGELOG.md +395 -377
  3. package/LICENSE +169 -169
  4. package/NOTICE +4 -4
  5. package/README.md +110 -110
  6. package/bin/create.js +540 -505
  7. package/bin/delegate.js +58 -57
  8. package/bin/nibula.js +302 -302
  9. package/docs/Assistant CLI.md +65 -65
  10. package/docs/Backend.md +201 -201
  11. package/docs/Components.md +140 -140
  12. package/docs/Creating pages.md +69 -69
  13. package/docs/Deploy.md +210 -210
  14. package/docs/Head and SEO.md +139 -139
  15. package/docs/Javascript.md +126 -126
  16. package/docs/Styling with SCSS.md +141 -139
  17. package/nginx.conf +113 -113
  18. package/package.json +69 -69
  19. package/src/backend/.htaccess +6 -6
  20. package/src/backend/_core/composer.json +5 -5
  21. package/src/backend/_core/composer.lock +492 -492
  22. package/src/backend/_core/index.js +267 -267
  23. package/src/backend/_core/index.php +160 -160
  24. package/src/backend/_core/init.js +52 -52
  25. package/src/backend/_core/init.php +33 -33
  26. package/src/backend/_core/modules/RateLimiter.js +58 -58
  27. package/src/backend/_core/modules/RateLimiter.php +30 -30
  28. package/src/backend/_core/modules/Response.js +59 -59
  29. package/src/backend/_core/modules/Response.php +48 -48
  30. package/src/backend/api/protected/example-protected.js +23 -23
  31. package/src/backend/api/protected/example-protected.php +16 -16
  32. package/src/backend/api/public/example-public.js +24 -24
  33. package/src/backend/api/public/example-public.php +16 -16
  34. package/src/backend/database/Database.js +46 -46
  35. package/src/backend/database/Database.php +23 -23
  36. package/src/backend/example.config.js +37 -37
  37. package/src/backend/example.config.php +37 -37
  38. package/src/backend/package.json +17 -17
  39. package/src/backend/web.config +43 -43
  40. package/src/frontend/.htaccess +51 -51
  41. package/src/frontend/404.njk +17 -17
  42. package/src/frontend/assets/brand/favicon.svg +54 -54
  43. package/src/frontend/assets/brand/logo.svg +54 -54
  44. package/src/frontend/components/global/footer.njk +25 -25
  45. package/src/frontend/components/global/header.njk +6 -6
  46. package/src/frontend/components/welcome.njk +115 -115
  47. package/src/frontend/index.njk +8 -8
  48. package/src/frontend/js/global.js +10 -10
  49. package/src/frontend/js/modules/exampleModule.js +2 -2
  50. package/src/frontend/js/pages/404.js +8 -8
  51. package/src/frontend/js/pages/homepage.js +8 -8
  52. package/src/frontend/layouts/base.njk +135 -136
  53. package/src/frontend/layouts/page-components.njk +13 -13
  54. package/src/frontend/llms.njk +17 -17
  55. package/src/frontend/robots.njk +7 -7
  56. package/src/frontend/scss/_global.scss +46 -46
  57. package/src/frontend/scss/_root.scss +13 -13
  58. package/src/frontend/scss/modules/_animations.scss +24 -24
  59. package/src/frontend/scss/modules/_footer.scss +28 -28
  60. package/src/frontend/scss/modules/_header.scss +26 -26
  61. package/src/frontend/scss/modules/_mobile.scss +29 -29
  62. package/src/frontend/scss/modules/_typography.scss +18 -18
  63. package/src/frontend/scss/modules/frameworks/_bootstrap.scss +109 -109
  64. package/src/frontend/scss/modules/frameworks/_bulma.scss +108 -108
  65. package/src/frontend/scss/modules/frameworks/_foundation.scss +137 -137
  66. package/src/frontend/scss/modules/frameworks/_uikit.scss +109 -109
  67. package/src/frontend/scss/pages/404.scss +27 -27
  68. package/src/frontend/scss/pages/homepage.scss +22 -22
  69. package/src/frontend/sitemap.njk +17 -17
  70. package/src/frontend/ts/global.ts +11 -11
  71. package/src/frontend/ts/modules/exampleModule.ts +2 -2
  72. package/src/frontend/ts/pages/404.ts +8 -8
  73. package/src/frontend/ts/pages/homepage.ts +8 -8
  74. package/src/frontend/web.config +51 -51
  75. package/tools/assistant.js +122 -122
  76. package/tools/buildJs.js +39 -39
  77. package/tools/cleanOutput.js +23 -23
  78. package/tools/cli/prompt.js +40 -40
  79. package/tools/cli/ui.js +87 -87
  80. package/tools/config/messages.json +80 -80
  81. package/tools/config/settings.json +130 -130
  82. package/tools/lib/colors.js +17 -17
  83. package/tools/lib/files.js +75 -75
  84. package/tools/lib/logger.js +22 -22
  85. package/tools/lib/outputPath.js +141 -141
  86. package/tools/lib/pageActions.js +146 -146
  87. package/tools/lib/pageArtifacts.js +46 -46
  88. package/tools/lib/pageComponents.js +88 -88
  89. package/tools/lib/pagesData.js +87 -87
  90. package/tools/lib/paths.js +63 -63
  91. package/tools/lib/project.js +54 -54
  92. package/tools/lib/text.js +47 -47
  93. package/tools/lib/validation.js +40 -40
  94. package/tools/res/templates/template.js +7 -7
  95. package/tools/res/templates/template.njk +8 -8
  96. package/tools/res/templates/template.scss +22 -22
  97. package/tools/res/templates/template.ts +8 -8
  98. package/tsconfig.json +24 -24
  99. package/src/frontend/scss/pages/prova.scss +0 -23
package/bin/nibula.js CHANGED
@@ -1,303 +1,303 @@
1
- #!/usr/bin/env node
2
-
3
- const fs = require('fs');
4
- const path = require('path');
5
- const https = require('https');
6
- const readline = require('readline');
7
- const { spawnSync } = require('child_process');
8
- const { findProjectRoot, NOT_INSIDE_PROJECT_MESSAGE } = require('../tools/lib/paths');
9
- const { color } = require('../tools/lib/colors');
10
- const { message } = require('../tools/lib/logger');
11
- const { delegateToLocal } = require('./delegate');
12
-
13
- const pkg = require('../package.json');
14
-
15
- const [, , cmd, ...rest] = process.argv;
16
-
17
- const CREATE = path.join(__dirname, 'create.js');
18
- const ASSISTANT = path.join(__dirname, '..', 'tools', 'assistant.js');
19
- const BUILDJS = path.join(__dirname, '..', 'tools', 'buildJs.js');
20
- const CLEAN = path.join(__dirname, '..', 'tools', 'cleanOutput.js');
21
-
22
- const REGISTRY = 'https://registry.npmjs.org/nibula/latest';
23
- const CHECK_TIMEOUT = 2500;
24
- const SKIP_FLAG = '--skip-update-check';
25
-
26
- function run(script, args) {
27
- const res = spawnSync('node', [script, ...args], {
28
- stdio: 'inherit',
29
- cwd: process.cwd(),
30
- });
31
- process.exit(res.status ?? 0);
32
- }
33
-
34
- function requireProjectRoot() {
35
- const root = findProjectRoot(process.cwd());
36
- if (!root) {
37
- console.error(NOT_INSIDE_PROJECT_MESSAGE);
38
- process.exit(1);
39
- }
40
- return root;
41
- }
42
-
43
- function requireOutsideProject() {
44
- const root = findProjectRoot(process.cwd());
45
- if (!root) return;
46
-
47
- console.error(`\n${color.red}${message('project.alreadyInside')}${color.reset}`);
48
- console.error(`${color.dim}${message('project.alreadyInsideHint')}${color.reset}\n`);
49
- process.exit(1);
50
- }
51
-
52
- function enterProject() {
53
- const root = requireProjectRoot();
54
- delegateToLocal(root);
55
- return root;
56
- }
57
-
58
- function runNpm(root, scriptName) {
59
- const res = spawnSync('npm', ['run', scriptName], {
60
- stdio: 'inherit',
61
- cwd: root,
62
- shell: process.platform === 'win32',
63
- });
64
- if (res.error) console.error(res.error.message);
65
- process.exit(res.status ?? 0);
66
- }
67
-
68
- function fetchLatest() {
69
- return new Promise((resolve) => {
70
- const req = https.get(REGISTRY, (res) => {
71
- if (res.statusCode !== 200) {
72
- res.resume();
73
- resolve(null);
74
- return;
75
- }
76
- let data = '';
77
- res.on('data', (chunk) => { data += chunk; });
78
- res.on('end', () => {
79
- try {
80
- resolve(JSON.parse(data).version ?? null);
81
- } catch {
82
- resolve(null);
83
- }
84
- });
85
- });
86
- req.on('error', () => resolve(null));
87
- req.setTimeout(CHECK_TIMEOUT, () => {
88
- req.destroy();
89
- resolve(null);
90
- });
91
- });
92
- }
93
-
94
- function isOlder(current, latest) {
95
- const parse = (v) => String(v).split('-')[0].split('.').map(Number);
96
- const a = parse(current);
97
- const b = parse(latest);
98
- for (let i = 0; i < 3; i++) {
99
- const na = a[i] || 0;
100
- const nb = b[i] || 0;
101
- if (na < nb) return true;
102
- if (na > nb) return false;
103
- }
104
- return false;
105
- }
106
-
107
- async function checkVersion() {
108
- const latest = await fetchLatest();
109
- return {
110
- current: pkg.version,
111
- latest,
112
- behind: latest !== null && isOlder(pkg.version, latest),
113
- };
114
- }
115
-
116
- function ask(question) {
117
- return new Promise((resolve) => {
118
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
119
- rl.question(question, (answer) => {
120
- rl.close();
121
- resolve(answer.trim().toLowerCase());
122
- });
123
- });
124
- }
125
-
126
- function updateGlobal(version) {
127
- const target = version ? `nibula@${version}` : 'nibula@latest';
128
- const res = spawnSync('npm', ['install', '-g', target, '--prefer-online'], {
129
- stdio: 'inherit',
130
- shell: process.platform === 'win32',
131
- });
132
- return res.status ?? 0;
133
- }
134
-
135
- function globalNibulaPath() {
136
- const res = spawnSync('npm', ['root', '-g'], {
137
- encoding: 'utf8',
138
- shell: process.platform === 'win32',
139
- });
140
- if (res.status !== 0 || !res.stdout) return null;
141
- const candidate = path.join(res.stdout.trim(), 'nibula', 'bin', 'nibula.js');
142
- return fs.existsSync(candidate) ? candidate : null;
143
- }
144
-
145
- function reexecAfterUpdate(args) {
146
- const target = globalNibulaPath();
147
- if (!target) {
148
- console.error(`${color.red}Could not locate the updated Nibula installation.${color.reset}`);
149
- console.error(`Run "nib new ${args[1]}" again to continue.`);
150
- return 1;
151
- }
152
- const res = spawnSync(process.execPath, [target, ...args, SKIP_FLAG], {
153
- stdio: 'inherit',
154
- cwd: process.cwd(),
155
- });
156
- return res.status ?? 0;
157
- }
158
-
159
- function findExistingProject(baseDir, projectName) {
160
- let entries;
161
- try {
162
- entries = fs.readdirSync(baseDir, { withFileTypes: true });
163
- } catch {
164
- return null;
165
- }
166
- for (const entry of entries) {
167
- if (!entry.isDirectory()) continue;
168
- const pkgPath = path.join(baseDir, entry.name, 'package.json');
169
- if (!fs.existsSync(pkgPath)) continue;
170
- try {
171
- const data = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
172
- if (data && data.name === projectName) {
173
- return path.join(baseDir, entry.name);
174
- }
175
- } catch {
176
- // ...
177
- }
178
- }
179
- return null;
180
- }
181
-
182
- function usage(currentVersion) {
183
- console.log(`
184
-
185
- ${color.bold}${color.cyan}Nibula (${currentVersion})${color.reset} ${color.bold}by Michele Garofalo${color.reset}
186
-
187
- ${color.yellow}nib new <project-name>${color.reset} Create your new project
188
- ${color.yellow}nib cli${color.reset} Open the page-management assistant
189
- ${color.yellow}nib run${color.reset} Start the dev server and builds out folder runtime
190
- ${color.yellow}nib build${color.reset} Build the site out folder to publish
191
- ${color.yellow}nib clean${color.reset} Remove the output directory
192
- ${color.yellow}nib update${color.reset} Update to the latest version
193
-
194
- `);
195
- }
196
-
197
- async function main() {
198
- switch (cmd) {
199
- case 'new': {
200
- const args = rest.filter((a) => a !== SKIP_FLAG);
201
- const skipCheck = rest.includes(SKIP_FLAG);
202
-
203
- if (!args[0]) {
204
- console.error('Missing project name. Usage: nib new <project-name>');
205
- process.exit(1);
206
- }
207
-
208
- requireOutsideProject();
209
-
210
- if (!skipCheck) {
211
- const info = await checkVersion();
212
- if (info.behind) {
213
- console.log(`\nA newer version of Nibula is available: ${info.current} → ${info.latest}`);
214
- if (process.stdin.isTTY) {
215
- const answer = await ask('Update before creating the project? [Y/n] ');
216
- if (answer === '' || answer === 'y' || answer === 'yes') {
217
- const code = updateGlobal(info.latest);
218
- if (code !== 0) process.exit(code);
219
- console.log(`\n${color.green}Updated.${color.reset} Continuing with the new version...\n`);
220
- process.exit(reexecAfterUpdate(['new', args[0]]));
221
- }
222
- } else {
223
- console.log('Run "nib update" to update.\n');
224
- }
225
- }
226
- }
227
-
228
- const existing = findExistingProject(process.cwd(), args[0]);
229
- if (existing) {
230
- console.log(`\n${color.yellow}A project named "${args[0]}" already exists:${color.reset} ${existing}`);
231
- let overwrite = false;
232
- if (process.stdin.isTTY) {
233
- const answer = await ask('Do you want to overwrite it? [y/N] ');
234
- overwrite = (answer === 'y' || answer === 'yes');
235
- } else {
236
- console.log('Run in a TTY to confirm overwrite.');
237
- }
238
- if (!overwrite) {
239
- console.log('Stopped creating the new project');
240
- process.exit(0);
241
- }
242
- try {
243
- fs.rmSync(existing, { recursive: true, force: true });
244
- console.log(`${color.green}Removed existing project.${color.reset}`);
245
- } catch (err) {
246
- console.error(`${color.red}Failed to remove existing project:${color.reset} ${err.message}`);
247
- process.exit(1);
248
- }
249
- }
250
-
251
- run(CREATE, [args[0]]);
252
- break;
253
- }
254
- case 'cli': {
255
- enterProject();
256
- run(ASSISTANT, []);
257
- break;
258
- }
259
- case 'run': {
260
- const root = enterProject();
261
- runNpm(root, 'serve');
262
- break;
263
- }
264
- case 'build': {
265
- const root = enterProject();
266
- runNpm(root, 'build');
267
- break;
268
- }
269
- case 'build-js': {
270
- enterProject();
271
- run(BUILDJS, rest);
272
- break;
273
- }
274
- case 'clean': {
275
- enterProject();
276
- run(CLEAN, []);
277
- break;
278
- }
279
- case 'update': {
280
- const info = await checkVersion();
281
- if (info.latest !== null && !info.behind) {
282
- console.log(`Already on the latest version (${info.current}).`);
283
- process.exit(0);
284
- }
285
- process.exit(updateGlobal(info.latest));
286
- }
287
- case undefined:
288
- case 'help':
289
- case '-help':
290
- case '--help':
291
- case 'h':
292
- case '-h': {
293
- usage(pkg.version);
294
- break;
295
- }
296
- default:
297
- console.error(`${color.red}\nUnknown command:${color.reset} ${cmd}`);
298
- usage(pkg.version);
299
- process.exit(1);
300
- }
301
- }
302
-
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const https = require('https');
6
+ const readline = require('readline');
7
+ const { spawnSync } = require('child_process');
8
+ const { findProjectRoot, NOT_INSIDE_PROJECT_MESSAGE } = require('../tools/lib/paths');
9
+ const { color } = require('../tools/lib/colors');
10
+ const { message } = require('../tools/lib/logger');
11
+ const { delegateToLocal } = require('./delegate');
12
+
13
+ const pkg = require('../package.json');
14
+
15
+ const [, , cmd, ...rest] = process.argv;
16
+
17
+ const CREATE = path.join(__dirname, 'create.js');
18
+ const ASSISTANT = path.join(__dirname, '..', 'tools', 'assistant.js');
19
+ const BUILDJS = path.join(__dirname, '..', 'tools', 'buildJs.js');
20
+ const CLEAN = path.join(__dirname, '..', 'tools', 'cleanOutput.js');
21
+
22
+ const REGISTRY = 'https://registry.npmjs.org/nibula/latest';
23
+ const CHECK_TIMEOUT = 2500;
24
+ const SKIP_FLAG = '--skip-update-check';
25
+
26
+ function run(script, args) {
27
+ const res = spawnSync('node', [script, ...args], {
28
+ stdio: 'inherit',
29
+ cwd: process.cwd(),
30
+ });
31
+ process.exit(res.status ?? 0);
32
+ }
33
+
34
+ function requireProjectRoot() {
35
+ const root = findProjectRoot(process.cwd());
36
+ if (!root) {
37
+ console.error(NOT_INSIDE_PROJECT_MESSAGE);
38
+ process.exit(1);
39
+ }
40
+ return root;
41
+ }
42
+
43
+ function requireOutsideProject() {
44
+ const root = findProjectRoot(process.cwd());
45
+ if (!root) return;
46
+
47
+ console.error(`\n${color.red}${message('project.alreadyInside')}${color.reset}`);
48
+ console.error(`${color.dim}${message('project.alreadyInsideHint')}${color.reset}\n`);
49
+ process.exit(1);
50
+ }
51
+
52
+ function enterProject() {
53
+ const root = requireProjectRoot();
54
+ delegateToLocal(root);
55
+ return root;
56
+ }
57
+
58
+ function runNpm(root, scriptName) {
59
+ const res = spawnSync('npm', ['run', scriptName], {
60
+ stdio: 'inherit',
61
+ cwd: root,
62
+ shell: process.platform === 'win32',
63
+ });
64
+ if (res.error) console.error(res.error.message);
65
+ process.exit(res.status ?? 0);
66
+ }
67
+
68
+ function fetchLatest() {
69
+ return new Promise((resolve) => {
70
+ const req = https.get(REGISTRY, (res) => {
71
+ if (res.statusCode !== 200) {
72
+ res.resume();
73
+ resolve(null);
74
+ return;
75
+ }
76
+ let data = '';
77
+ res.on('data', (chunk) => { data += chunk; });
78
+ res.on('end', () => {
79
+ try {
80
+ resolve(JSON.parse(data).version ?? null);
81
+ } catch {
82
+ resolve(null);
83
+ }
84
+ });
85
+ });
86
+ req.on('error', () => resolve(null));
87
+ req.setTimeout(CHECK_TIMEOUT, () => {
88
+ req.destroy();
89
+ resolve(null);
90
+ });
91
+ });
92
+ }
93
+
94
+ function isOlder(current, latest) {
95
+ const parse = (v) => String(v).split('-')[0].split('.').map(Number);
96
+ const a = parse(current);
97
+ const b = parse(latest);
98
+ for (let i = 0; i < 3; i++) {
99
+ const na = a[i] || 0;
100
+ const nb = b[i] || 0;
101
+ if (na < nb) return true;
102
+ if (na > nb) return false;
103
+ }
104
+ return false;
105
+ }
106
+
107
+ async function checkVersion() {
108
+ const latest = await fetchLatest();
109
+ return {
110
+ current: pkg.version,
111
+ latest,
112
+ behind: latest !== null && isOlder(pkg.version, latest),
113
+ };
114
+ }
115
+
116
+ function ask(question) {
117
+ return new Promise((resolve) => {
118
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
119
+ rl.question(question, (answer) => {
120
+ rl.close();
121
+ resolve(answer.trim().toLowerCase());
122
+ });
123
+ });
124
+ }
125
+
126
+ function updateGlobal(version) {
127
+ const target = version ? `nibula@${version}` : 'nibula@latest';
128
+ const res = spawnSync('npm', ['install', '-g', target, '--prefer-online'], {
129
+ stdio: 'inherit',
130
+ shell: process.platform === 'win32',
131
+ });
132
+ return res.status ?? 0;
133
+ }
134
+
135
+ function globalNibulaPath() {
136
+ const res = spawnSync('npm', ['root', '-g'], {
137
+ encoding: 'utf8',
138
+ shell: process.platform === 'win32',
139
+ });
140
+ if (res.status !== 0 || !res.stdout) return null;
141
+ const candidate = path.join(res.stdout.trim(), 'nibula', 'bin', 'nibula.js');
142
+ return fs.existsSync(candidate) ? candidate : null;
143
+ }
144
+
145
+ function reexecAfterUpdate(args) {
146
+ const target = globalNibulaPath();
147
+ if (!target) {
148
+ console.error(`${color.red}Could not locate the updated Nibula installation.${color.reset}`);
149
+ console.error(`Run "nib new ${args[1]}" again to continue.`);
150
+ return 1;
151
+ }
152
+ const res = spawnSync(process.execPath, [target, ...args, SKIP_FLAG], {
153
+ stdio: 'inherit',
154
+ cwd: process.cwd(),
155
+ });
156
+ return res.status ?? 0;
157
+ }
158
+
159
+ function findExistingProject(baseDir, projectName) {
160
+ let entries;
161
+ try {
162
+ entries = fs.readdirSync(baseDir, { withFileTypes: true });
163
+ } catch {
164
+ return null;
165
+ }
166
+ for (const entry of entries) {
167
+ if (!entry.isDirectory()) continue;
168
+ const pkgPath = path.join(baseDir, entry.name, 'package.json');
169
+ if (!fs.existsSync(pkgPath)) continue;
170
+ try {
171
+ const data = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
172
+ if (data && data.name === projectName) {
173
+ return path.join(baseDir, entry.name);
174
+ }
175
+ } catch {
176
+ // ...
177
+ }
178
+ }
179
+ return null;
180
+ }
181
+
182
+ function usage(currentVersion) {
183
+ console.log(`
184
+
185
+ ${color.bold}${color.cyan}Nibula (${currentVersion})${color.reset} ${color.bold}by Michele Garofalo${color.reset}
186
+
187
+ ${color.yellow}nib new <project-name>${color.reset} Create your new project
188
+ ${color.yellow}nib cli${color.reset} Open the page-management assistant
189
+ ${color.yellow}nib run${color.reset} Start the dev server and builds out folder runtime
190
+ ${color.yellow}nib build${color.reset} Build the site out folder to publish
191
+ ${color.yellow}nib clean${color.reset} Remove the output directory
192
+ ${color.yellow}nib update${color.reset} Update to the latest version
193
+
194
+ `);
195
+ }
196
+
197
+ async function main() {
198
+ switch (cmd) {
199
+ case 'new': {
200
+ const args = rest.filter((a) => a !== SKIP_FLAG);
201
+ const skipCheck = rest.includes(SKIP_FLAG);
202
+
203
+ if (!args[0]) {
204
+ console.error('Missing project name. Usage: nib new <project-name>');
205
+ process.exit(1);
206
+ }
207
+
208
+ requireOutsideProject();
209
+
210
+ if (!skipCheck) {
211
+ const info = await checkVersion();
212
+ if (info.behind) {
213
+ console.log(`\nA newer version of Nibula is available: ${info.current} → ${info.latest}`);
214
+ if (process.stdin.isTTY) {
215
+ const answer = await ask('Update before creating the project? [Y/n] ');
216
+ if (answer === '' || answer === 'y' || answer === 'yes') {
217
+ const code = updateGlobal(info.latest);
218
+ if (code !== 0) process.exit(code);
219
+ console.log(`\n${color.green}Updated.${color.reset} Continuing with the new version...\n`);
220
+ process.exit(reexecAfterUpdate(['new', args[0]]));
221
+ }
222
+ } else {
223
+ console.log('Run "nib update" to update.\n');
224
+ }
225
+ }
226
+ }
227
+
228
+ const existing = findExistingProject(process.cwd(), args[0]);
229
+ if (existing) {
230
+ console.log(`\n${color.yellow}A project named "${args[0]}" already exists:${color.reset} ${existing}`);
231
+ let overwrite = false;
232
+ if (process.stdin.isTTY) {
233
+ const answer = await ask('Do you want to overwrite it? [y/N] ');
234
+ overwrite = (answer === 'y' || answer === 'yes');
235
+ } else {
236
+ console.log('Run in a TTY to confirm overwrite.');
237
+ }
238
+ if (!overwrite) {
239
+ console.log('Stopped creating the new project');
240
+ process.exit(0);
241
+ }
242
+ try {
243
+ fs.rmSync(existing, { recursive: true, force: true });
244
+ console.log(`${color.green}Removed existing project.${color.reset}`);
245
+ } catch (err) {
246
+ console.error(`${color.red}Failed to remove existing project:${color.reset} ${err.message}`);
247
+ process.exit(1);
248
+ }
249
+ }
250
+
251
+ run(CREATE, [args[0]]);
252
+ break;
253
+ }
254
+ case 'cli': {
255
+ enterProject();
256
+ run(ASSISTANT, []);
257
+ break;
258
+ }
259
+ case 'run': {
260
+ const root = enterProject();
261
+ runNpm(root, 'serve');
262
+ break;
263
+ }
264
+ case 'build': {
265
+ const root = enterProject();
266
+ runNpm(root, 'build');
267
+ break;
268
+ }
269
+ case 'build-js': {
270
+ enterProject();
271
+ run(BUILDJS, rest);
272
+ break;
273
+ }
274
+ case 'clean': {
275
+ enterProject();
276
+ run(CLEAN, []);
277
+ break;
278
+ }
279
+ case 'update': {
280
+ const info = await checkVersion();
281
+ if (info.latest !== null && !info.behind) {
282
+ console.log(`Already on the latest version (${info.current}).`);
283
+ process.exit(0);
284
+ }
285
+ process.exit(updateGlobal(info.latest));
286
+ }
287
+ case undefined:
288
+ case 'help':
289
+ case '-help':
290
+ case '--help':
291
+ case 'h':
292
+ case '-h': {
293
+ usage(pkg.version);
294
+ break;
295
+ }
296
+ default:
297
+ console.error(`${color.red}\nUnknown command:${color.reset} ${cmd}`);
298
+ usage(pkg.version);
299
+ process.exit(1);
300
+ }
301
+ }
302
+
303
303
  main();