@haxtheweb/create 9.0.8 → 9.0.9

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.
@@ -0,0 +1,517 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.siteActions = siteActions;
8
+ exports.siteCommandDetected = siteCommandDetected;
9
+ exports.siteItemsOptionsList = siteItemsOptionsList;
10
+ exports.siteNodeOperations = siteNodeOperations;
11
+ exports.siteProcess = siteProcess;
12
+ exports.siteThemeList = siteThemeList;
13
+ var _promises = require("node:timers/promises");
14
+ var p = _interopRequireWildcard(require("@clack/prompts"));
15
+ var _picocolors = _interopRequireDefault(require("picocolors"));
16
+ var _statements = require("../statements.js");
17
+ var haxcmsNodejsCli = _interopRequireWildcard(require("@haxtheweb/haxcms-nodejs/dist/cli.js"));
18
+ var hax = _interopRequireWildcard(require("@haxtheweb/haxcms-nodejs"));
19
+ var child_process = _interopRequireWildcard(require("child_process"));
20
+ var util = _interopRequireWildcard(require("node:util"));
21
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
22
+ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
23
+ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
24
+ const HAXCMS = hax.HAXCMS;
25
+ const exec = util.promisify(child_process.exec);
26
+ var sysSurge = true;
27
+ exec('surge --version', error => {
28
+ if (error) {
29
+ sysSurge = false;
30
+ }
31
+ });
32
+ const fakeSend = {
33
+ send: json => console.log(json),
34
+ sendStatus: data => console.log(data)
35
+ };
36
+ function siteActions() {
37
+ return [{
38
+ value: 'start',
39
+ label: "Start site (http://localhost)"
40
+ }, {
41
+ value: 'status',
42
+ label: "Status"
43
+ }, {
44
+ value: 'sync',
45
+ label: "Sync git"
46
+ }, {
47
+ value: 'theme',
48
+ label: "Change theme"
49
+ }, {
50
+ value: 'node:add',
51
+ label: "Add page"
52
+ }, {
53
+ value: 'node:edit',
54
+ label: "Edit page"
55
+ }, {
56
+ value: 'node:delete',
57
+ label: "Delete page"
58
+ }];
59
+ }
60
+ async function siteCommandDetected(commandRun) {
61
+ var activeHaxsite = await hax.systemStructureContext();
62
+ // default to status unless already set so we don't issue a create in a create
63
+ if (!commandRun.arguments.action) {
64
+ commandRun.arguments.action = 'status';
65
+ }
66
+ p.intro(`${_picocolors.default.bgBlack(_picocolors.default.white(` HAXTheWeb : Site detected `))}`);
67
+ commandRun.command = "site";
68
+ p.intro(`${_picocolors.default.bgBlue(_picocolors.default.white(` Name: ${activeHaxsite.name} `))}`);
69
+ // defaults if nothing set via CLI
70
+ let operation = {
71
+ ...commandRun.arguments,
72
+ ...commandRun.options
73
+ };
74
+ if (!commandRun.options.title) {
75
+ commandRun.options.title = "New Page";
76
+ }
77
+ if (!commandRun.options.domain && commandRun.options.y) {
78
+ commandRun.options.domain = `haxcli-${activeHaxsite.name}.surge.sh`;
79
+ }
80
+ // infinite loop until quitting the cli
81
+ while (operation.action !== 'quit') {
82
+ let actions = siteActions();
83
+ if (sysSurge) {
84
+ actions.push({
85
+ value: 'surge',
86
+ label: "Publish site using Surge.sh"
87
+ });
88
+ }
89
+ actions.push({
90
+ value: 'quit',
91
+ label: "🚪 Quit"
92
+ });
93
+ if (!operation.action) {
94
+ commandRun = {
95
+ command: null,
96
+ arguments: {},
97
+ options: {}
98
+ };
99
+ // ensures data is updated and stateful per action
100
+ activeHaxsite = await hax.systemStructureContext();
101
+ operation = await p.group({
102
+ action: ({
103
+ results
104
+ }) => p.select({
105
+ message: `Actions you can take`,
106
+ options: actions
107
+ })
108
+ }, {
109
+ onCancel: () => {
110
+ p.cancel('🧙 Merlin: Canceling CLI.. HAX ya later 🪄');
111
+ (0, _statements.communityStatement)();
112
+ process.exit(0);
113
+ }
114
+ });
115
+ }
116
+ switch (operation.action) {
117
+ case "status":
118
+ p.intro(`${_picocolors.default.bgBlue(_picocolors.default.white(` Title: ${activeHaxsite.manifest.title} `))}`);
119
+ p.intro(`${_picocolors.default.bgBlue(_picocolors.default.white(` Description: ${activeHaxsite.manifest.description} `))}`);
120
+ p.intro(`${_picocolors.default.bgBlue(_picocolors.default.white(` Theme: ${activeHaxsite.manifest.metadata.theme.name} (${activeHaxsite.manifest.metadata.theme.element})`))}`);
121
+ p.intro(`${_picocolors.default.bgBlue(_picocolors.default.white(` Pages: ${activeHaxsite.manifest.items.length} `))}`);
122
+ const date = new Date(activeHaxsite.manifest.metadata.site.updated * 1000);
123
+ p.intro(`${_picocolors.default.bgBlue(_picocolors.default.white(` Last updated: ${date.toLocaleDateString("en-US")} `))}`);
124
+ break;
125
+ case "start":
126
+ try {
127
+ p.intro(`Starting server.. `);
128
+ p.intro(`⌨️ To stop server, press: ${_picocolors.default.bold(_picocolors.default.black(_picocolors.default.bgRed(` CTRL + C `)))}`);
129
+ await exec(`cd ${activeHaxsite.directory} && npx @haxtheweb/haxcms-nodejs`);
130
+ } catch (e) {
131
+ console.log(e.stderr);
132
+ }
133
+ break;
134
+ case "node:add":
135
+ try {
136
+ // @todo accept title if not supplied
137
+ if (!commandRun.options.title) {
138
+ commandRun.options.title = await p.text({
139
+ message: `Title for this page`,
140
+ placeholder: "New page",
141
+ required: true,
142
+ validate: value => {
143
+ if (!value) {
144
+ return "Title must be set (tab writes default)";
145
+ }
146
+ }
147
+ });
148
+ }
149
+ let resp = await haxcmsNodejsCli.cliBridge('createNode', {
150
+ site: activeHaxsite,
151
+ node: {
152
+ title: commandRun.options.title
153
+ }
154
+ });
155
+ if (commandRun.options.v) {
156
+ console.log(resp.res.data);
157
+ }
158
+ console.log(`"${commandRun.options.title}" added to site`);
159
+ } catch (e) {
160
+ console.log(e.stderr);
161
+ }
162
+ break;
163
+ case "node:edit":
164
+ try {
165
+ if (!commandRun.options.itemId) {
166
+ commandRun.options.itemId = await p.select({
167
+ message: `Select an item to edit`,
168
+ required: true,
169
+ options: [{
170
+ value: null,
171
+ label: "-- edit nothing, exit --"
172
+ }, ...(await siteItemsOptionsList(activeHaxsite))]
173
+ });
174
+ }
175
+ if (commandRun.options.itemId) {
176
+ let nodeOps = siteNodeOperations();
177
+ let page = activeHaxsite.loadNode(commandRun.options.itemId);
178
+ // select which aspect of this we are editing
179
+ if (!commandRun.options.nodeOp) {
180
+ commandRun.options.nodeOp = await p.select({
181
+ message: `${page.title} (${page.id}) - Node operations`,
182
+ required: true,
183
+ options: [{
184
+ value: null,
185
+ label: "-- Exit --"
186
+ }, ...nodeOps]
187
+ });
188
+ }
189
+ if (commandRun.options.nodeOp && siteNodeOperations(commandRun.options.nodeOp)) {
190
+ let nodeProp = commandRun.options.nodeOp;
191
+ var propValue = commandRun.options[nodeProp];
192
+ // verify we have a setting for the operation requested
193
+ // otherwise we get interactive again
194
+ if (!commandRun.options[nodeProp]) {
195
+ let val = page[nodeProp];
196
+ if (['tags', 'published', 'hideInMenu', 'theme'].includes(nodeProp)) {
197
+ val = page.metadata[nodeProp];
198
+ } else if (nodeProp === 'content') {
199
+ val = await activeHaxsite.getPageContent(page);
200
+ }
201
+ // boolean is confirm
202
+ if (['published', 'hideInMenu'].includes(nodeProp)) {
203
+ propValue = await p.confirm({
204
+ message: `${nodeProp}:`,
205
+ initialValue: Boolean(val),
206
+ defaultValue: Boolean(val)
207
+ });
208
+ }
209
+ // these have fixed possible values
210
+ else if (['parent', 'theme'].includes(nodeProp)) {
211
+ let l = nodeProp === 'parent' ? "-- no parent --" : "-- no theme --";
212
+ let list = nodeProp === 'parent' ? await siteItemsOptionsList(activeHaxsite, page.id) : await siteThemeList();
213
+ propValue = await p.select({
214
+ message: `${nodeProp}:`,
215
+ defaultValue: val,
216
+ initialValue: val,
217
+ options: [{
218
+ value: null,
219
+ label: l
220
+ }, ...list]
221
+ });
222
+ } else {
223
+ propValue = await p.text({
224
+ message: `${nodeProp}:`,
225
+ initialValue: val,
226
+ defaultValue: val
227
+ });
228
+ }
229
+ }
230
+ if (nodeProp === 'order') {
231
+ propValue = parseInt(propValue);
232
+ }
233
+ // account for CLI
234
+ if (propValue === "null") {
235
+ propValue = null;
236
+ }
237
+ commandRun.options[nodeProp] = propValue;
238
+ }
239
+ // ensure we set empty values, just not completely undefined values
240
+ if (typeof commandRun.options[commandRun.options.nodeOp] !== "undefined") {
241
+ if (commandRun.options.nodeOp === 'content') {
242
+ if (commandRun.options.content && (await page.writeLocation(commandRun.options.content))) {
243
+ console.log(`node:edit success updated page content: "${page.id}`);
244
+ } else {
245
+ console.warn(`node:edit failure to write page content : ${page.id}`);
246
+ }
247
+ } else {
248
+ if (['tags', 'published', 'hideInMenu'].includes(commandRun.options.nodeOp)) {
249
+ page.metadata[commandRun.options.nodeOp] = commandRun.options[commandRun.options.nodeOp];
250
+ } else if (commandRun.options.nodeOp === 'theme') {
251
+ let themes = await HAXCMS.getThemes();
252
+ page.metadata.theme = themes[commandRun.options[commandRun.options.nodeOp]];
253
+ } else {
254
+ page[commandRun.options.nodeOp] = commandRun.options[commandRun.options.nodeOp];
255
+ }
256
+ let resp = await activeHaxsite.updateNode(page);
257
+ if (commandRun.options.v) {
258
+ console.log(resp);
259
+ }
260
+ }
261
+ }
262
+ }
263
+ } catch (e) {
264
+ console.log(e.stderr);
265
+ }
266
+ break;
267
+ case "node:delete":
268
+ try {
269
+ if (!commandRun.options.itemId) {
270
+ commandRun.options.itemId = await p.select({
271
+ message: `Select an item to delete`,
272
+ required: true,
273
+ options: [{
274
+ value: null,
275
+ label: "-- Delete nothing, exit --"
276
+ }, ...(await siteItemsOptionsList(activeHaxsite))]
277
+ });
278
+ }
279
+ if (commandRun.options.itemId) {
280
+ let del = false;
281
+ if (!commandRun.options.y) {
282
+ del = await p.confirm({
283
+ message: `Are you sure you want to delete ${commandRun.options.itemId}? (This cannot be undone)`,
284
+ initialValue: true
285
+ });
286
+ } else {
287
+ del = true;
288
+ }
289
+ // extra confirmation given destructive operation
290
+ if (del) {
291
+ let resp = await haxcmsNodejsCli.cliBridge('deleteNode', {
292
+ site: activeHaxsite,
293
+ node: {
294
+ id: commandRun.options.itemId
295
+ }
296
+ });
297
+ if (resp.res.data === 500) {
298
+ console.warn(`node:delete failed "${commandRun.options.itemId} not found`);
299
+ } else {
300
+ console.log(`"${commandRun.options.itemId}" deleted`);
301
+ }
302
+ } else {
303
+ console.log(`Delete operation canceled`);
304
+ }
305
+ }
306
+ } catch (e) {
307
+ console.log(e.stderr);
308
+ }
309
+ break;
310
+ case "sync":
311
+ // @todo git sync might need other arguments / be combined with publishing
312
+ try {
313
+ await exec(`cd ${activeHaxsite.directory} && git pull && git push`);
314
+ } catch (e) {
315
+ console.log(e.stderr);
316
+ }
317
+ break;
318
+ case "theme":
319
+ try {
320
+ //theme
321
+ let list = await siteThemeList();
322
+ activeHaxsite = await hax.systemStructureContext();
323
+ let val = activeHaxsite.manifest.metadata.theme.element;
324
+ if (!commandRun.options.theme) {
325
+ commandRun.options.theme = await p.select({
326
+ message: `Select theme:`,
327
+ defaultValue: val,
328
+ initialValue: val,
329
+ options: list
330
+ });
331
+ let themes = await HAXCMS.getThemes();
332
+ if (themes && commandRun.options.theme && themes[commandRun.options.theme]) {
333
+ activeHaxsite.manifest.metadata.theme = themes[commandRun.options.theme];
334
+ activeHaxsite.manifest.save(false);
335
+ }
336
+ }
337
+ } catch (e) {
338
+ console.log(e.stderr);
339
+ }
340
+ break;
341
+ case "surge":
342
+ try {
343
+ if (!commandRun.options.domain) {
344
+ commandRun.options.domain = await p.text({
345
+ message: `Domain for surge`,
346
+ defaultValue: `haxcli-${activeHaxsite.name}.surge.sh`,
347
+ required: true,
348
+ validate: value => {
349
+ if (!value) {
350
+ return "Domain must have a value";
351
+ }
352
+ }
353
+ });
354
+ }
355
+ let execOutput = await exec(`cd ${activeHaxsite.directory} && surge . ${commandRun.options.domain}`);
356
+ console.log(execOutput.stdout.trim());
357
+ } catch (e) {
358
+ console.log(e.stderr);
359
+ }
360
+ break;
361
+ case "quit":
362
+ // quit
363
+ process.exit(0);
364
+ break;
365
+ }
366
+ if (commandRun.options.y) {
367
+ process.exit(0);
368
+ }
369
+ operation.action = null;
370
+ }
371
+ (0, _statements.communityStatement)();
372
+ }
373
+ function siteNodeOperations(search = null) {
374
+ let obj = [{
375
+ value: 'title',
376
+ label: "Title"
377
+ }, {
378
+ value: 'content',
379
+ label: "Page content"
380
+ }, {
381
+ value: 'slug',
382
+ label: "Path (slug)"
383
+ }, {
384
+ value: 'published',
385
+ label: "Publishing status"
386
+ }, {
387
+ value: 'tags',
388
+ label: "Tags"
389
+ }, {
390
+ value: 'parent',
391
+ label: "Parent"
392
+ }, {
393
+ value: 'order',
394
+ label: "Order"
395
+ }, {
396
+ value: 'theme',
397
+ label: "Theme"
398
+ }, {
399
+ value: 'hideInMenu',
400
+ label: "Hide in menu"
401
+ }];
402
+ if (search) {
403
+ for (const op of obj) {
404
+ if (op.value === search) {
405
+ return true;
406
+ }
407
+ }
408
+ return false;
409
+ }
410
+ return obj;
411
+ }
412
+
413
+ // process site creation
414
+ async function siteProcess(commandRun, project, port = '3000') {
415
+ // auto select operations to perform if requested
416
+ if (!project.extras) {
417
+ let extras = ['launch'];
418
+ project.extras = extras;
419
+ }
420
+ let s = p.spinner();
421
+ s.start((0, _statements.merlinSays)(`Creating new site: ${project.name}`));
422
+ let siteRequest = {
423
+ "site": {
424
+ "name": project.name,
425
+ "description": "own course",
426
+ "theme": commandRun.options.theme ? commandRun.options.theme : "clean-one"
427
+ },
428
+ "build": {
429
+ "type": "own",
430
+ "structure": "course",
431
+ "items": null,
432
+ "files": null
433
+ },
434
+ "theme": {
435
+ "color": "green",
436
+ "icon": "av:library-add"
437
+ }
438
+ };
439
+ HAXCMS.cliWritePath = `${project.path}`;
440
+ await hax.RoutesMap.post.createSite({
441
+ body: siteRequest
442
+ }, fakeSend);
443
+ s.stop((0, _statements.merlinSays)(`${project.name} created!`));
444
+ await (0, _promises.setTimeout)(500);
445
+ if (project.gitRepo && !commandRun.options.isMonorepo) {
446
+ try {
447
+ await exec(`cd ${project.path}/${project.name} && git init && git add -A && git commit -m "first commit" && git branch -M main${project.gitRepo ? ` && git remote add origin ${project.gitRepo}` : ''}`);
448
+ } catch (e) {}
449
+ }
450
+ // options for install, git and other extras
451
+ // can't launch if we didn't install first so launch implies installation
452
+ if (project.extras.includes('launch') || project.extras.includes('install')) {
453
+ s.start((0, _statements.merlinSays)(`Installation magic (${commandRun.options.npmClient} install)`));
454
+ try {
455
+ // monorepos install from top but then still need to launch from local location
456
+ if (!commandRun.options.isMonorepo) {
457
+ await exec(`cd ${project.path}/${project.name} && ${commandRun.options.npmClient} install`);
458
+ }
459
+ } catch (e) {
460
+ console.log(e);
461
+ }
462
+ s.stop((0, _statements.merlinSays)(`Everything is installed. It's go time`));
463
+ }
464
+ // autolaunch if default was selected
465
+ if (project.extras.includes('launch')) {
466
+ let optionPath = `${project.path}/${project.name}`;
467
+ let command = `npx @haxtheweb/haxcms-nodejs`;
468
+ p.note(`${(0, _statements.merlinSays)(`I have summoned a sub-process daemon 👹`)}
469
+
470
+ 🚀 Running your ${_picocolors.default.bold(project.type)} ${_picocolors.default.bold(project.name)}:
471
+ ${_picocolors.default.underline(_picocolors.default.cyan(`http://localhost:${port}`))}
472
+
473
+ 🏠 Launched: ${_picocolors.default.underline(_picocolors.default.bold(_picocolors.default.yellow(_picocolors.default.bgBlack(`${optionPath}`))))}
474
+ 💻 Folder: ${_picocolors.default.bold(_picocolors.default.yellow(_picocolors.default.bgBlack(`cd ${optionPath}`)))}
475
+ 📂 Open folder: ${_picocolors.default.bold(_picocolors.default.yellow(_picocolors.default.bgBlack(`open ${optionPath}`)))}
476
+ 📘 VS Code Project: ${_picocolors.default.bold(_picocolors.default.yellow(_picocolors.default.bgBlack(`code ${optionPath}`)))}
477
+ 🚧 Launch later: ${_picocolors.default.bold(_picocolors.default.yellow(_picocolors.default.bgBlack(`${command}`)))}
478
+
479
+ ⌨️ To resume 🧙 Merlin press: ${_picocolors.default.bold(_picocolors.default.black(_picocolors.default.bgRed(` CTRL + C `)))}
480
+ `);
481
+ // at least a second to see the message print at all
482
+ await (0, _promises.setTimeout)(1000);
483
+ try {
484
+ await exec(`cd ${optionPath} && ${command}`);
485
+ } catch (e) {
486
+ // don't log bc output is weird
487
+ }
488
+ } else {
489
+ let nextSteps = `cd ${project.path}/${project.name} && ${project.extras.includes('install') ? '' : `${commandRun.options.npmClient} install && `}${commandRun.options.npmClient} start`;
490
+ p.note(`${project.name} is ready to go. Run the following to start development:`);
491
+ p.outro(nextSteps);
492
+ }
493
+ }
494
+ async function siteItemsOptionsList(activeHaxsite, skipId = null) {
495
+ let items = [];
496
+ for (var i in activeHaxsite.manifest.items) {
497
+ // ensure we remove self if operation is about page in question like parent selector
498
+ if (activeHaxsite.manifest.items[i].id !== skipId) {
499
+ items.push({
500
+ value: activeHaxsite.manifest.items[i].id,
501
+ label: activeHaxsite.manifest.items[i].title
502
+ });
503
+ }
504
+ }
505
+ return items;
506
+ }
507
+ async function siteThemeList() {
508
+ let themes = await HAXCMS.getThemes();
509
+ let items = [];
510
+ for (var i in themes) {
511
+ items.push({
512
+ value: i,
513
+ label: themes[i].name
514
+ });
515
+ }
516
+ return items;
517
+ }