nibula 1.2.3 → 1.2.5

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