@haxtheweb/create 9.0.10 → 9.0.12

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.
@@ -8,12 +8,16 @@ exports.siteActions = siteActions;
8
8
  exports.siteCommandDetected = siteCommandDetected;
9
9
  exports.siteItemsOptionsList = siteItemsOptionsList;
10
10
  exports.siteNodeOperations = siteNodeOperations;
11
+ exports.siteNodeStatsOperations = siteNodeStatsOperations;
11
12
  exports.siteProcess = siteProcess;
12
13
  exports.siteThemeList = siteThemeList;
13
14
  var _promises = require("node:timers/promises");
14
15
  var p = _interopRequireWildcard(require("@clack/prompts"));
15
16
  var _picocolors = _interopRequireDefault(require("picocolors"));
17
+ var _jsYaml = require("js-yaml");
18
+ var _nodeHtmlParser = require("node-html-parser");
16
19
  var _statements = require("../statements.js");
20
+ var _microFrontendRegistry = require("../micro-frontend-registry.js");
17
21
  var haxcmsNodejsCli = _interopRequireWildcard(require("@haxtheweb/haxcms-nodejs/dist/cli.js"));
18
22
  var hax = _interopRequireWildcard(require("@haxtheweb/haxcms-nodejs"));
19
23
  var child_process = _interopRequireWildcard(require("child_process"));
@@ -21,6 +25,12 @@ var util = _interopRequireWildcard(require("node:util"));
21
25
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
22
26
  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
27
  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; }
28
+ // trick MFR into giving local paths
29
+ globalThis.MicroFrontendRegistryConfig = {
30
+ base: `@haxtheweb/open-apis/`
31
+ };
32
+ // emable HAXcms routes so we have name => path just like on frontend!
33
+ _microFrontendRegistry.MicroFrontendRegistry.enableServices(['haxcms']);
24
34
  const HAXCMS = hax.HAXCMS;
25
35
  const exec = util.promisify(child_process.exec);
26
36
  var sysSurge = true;
@@ -29,10 +39,27 @@ exec('surge --version', error => {
29
39
  sysSurge = false;
30
40
  }
31
41
  });
32
- const fakeSend = {
33
- send: json => console.log(json),
34
- sendStatus: data => console.log(data)
35
- };
42
+
43
+ // fake response class so we can capture the response from the headless route as opposed to print to console
44
+ // this way we can handle as data or if use is requesting output format to change we can respond
45
+ class Res {
46
+ constructor() {
47
+ this.query = {};
48
+ this.data = null;
49
+ this.statusCode = null;
50
+ }
51
+ send(data) {
52
+ this.data = data;
53
+ return this;
54
+ }
55
+ status(status) {
56
+ this.statusCode = status;
57
+ return this;
58
+ }
59
+ setHeader() {
60
+ return this;
61
+ }
62
+ }
36
63
  function siteActions() {
37
64
  return [{
38
65
  value: 'start',
@@ -46,6 +73,9 @@ function siteActions() {
46
73
  }, {
47
74
  value: 'theme',
48
75
  label: "Change theme"
76
+ }, {
77
+ value: 'node:stats',
78
+ label: "Page stats"
49
79
  }, {
50
80
  value: 'node:add',
51
81
  label: "Add page"
@@ -55,6 +85,9 @@ function siteActions() {
55
85
  }, {
56
86
  value: 'node:delete',
57
87
  label: "Delete page"
88
+ }, {
89
+ value: 'file:list',
90
+ label: "List files"
58
91
  }];
59
92
  }
60
93
  async function siteCommandDetected(commandRun) {
@@ -131,6 +164,56 @@ async function siteCommandDetected(commandRun) {
131
164
  console.log(e.stderr);
132
165
  }
133
166
  break;
167
+ case "node:stats":
168
+ try {
169
+ if (!commandRun.options.itemId) {
170
+ commandRun.options.itemId = await p.select({
171
+ message: `Select an item to edit`,
172
+ required: true,
173
+ options: [{
174
+ value: null,
175
+ label: "-- edit nothing, exit --"
176
+ }, ...(await siteItemsOptionsList(activeHaxsite))]
177
+ });
178
+ }
179
+ if (commandRun.options.itemId) {
180
+ let nodeOps = siteNodeStatsOperations();
181
+ let page = activeHaxsite.loadNode(commandRun.options.itemId);
182
+ // select which aspect of this we are editing
183
+ if (!commandRun.options.nodeOp) {
184
+ commandRun.options.nodeOp = await p.select({
185
+ message: `${page.title} (${page.id}) - Node operations`,
186
+ required: true,
187
+ options: [{
188
+ value: null,
189
+ label: "-- Exit --"
190
+ }, ...nodeOps]
191
+ });
192
+ }
193
+ if (commandRun.options.nodeOp && siteNodeStatsOperations(commandRun.options.nodeOp)) {
194
+ switch (commandRun.options.nodeOp) {
195
+ case 'details':
196
+ console.log(page);
197
+ break;
198
+ case 'html':
199
+ console.log(await activeHaxsite.getPageContent(page));
200
+ break;
201
+ case 'schema':
202
+ // next up
203
+ let html = await activeHaxsite.getPageContent(page);
204
+ let dom = (0, _nodeHtmlParser.parse)(html);
205
+ console.log(dom);
206
+ break;
207
+ case 'md':
208
+ // @todo use the built in endpoints broker
209
+ break;
210
+ }
211
+ }
212
+ }
213
+ } catch (e) {
214
+ console.log(e.stderr);
215
+ }
216
+ break;
134
217
  case "node:add":
135
218
  try {
136
219
  // @todo accept title if not supplied
@@ -358,6 +441,18 @@ async function siteCommandDetected(commandRun) {
358
441
  console.log(e.stderr);
359
442
  }
360
443
  break;
444
+ case "file:list":
445
+ let res = new Res();
446
+ await hax.RoutesMap.get.listFiles({
447
+ query: activeHaxsite.name,
448
+ filename: commandRun.options.filename
449
+ }, res);
450
+ if (commandRun.options.format === 'yaml') {
451
+ console.log((0, _jsYaml.dump)(res.data));
452
+ } else {
453
+ console.log(res.data);
454
+ }
455
+ break;
361
456
  case "quit":
362
457
  // quit
363
458
  process.exit(0);
@@ -370,6 +465,30 @@ async function siteCommandDetected(commandRun) {
370
465
  }
371
466
  (0, _statements.communityStatement)();
372
467
  }
468
+ function siteNodeStatsOperations(search = null) {
469
+ let obj = [{
470
+ value: 'details',
471
+ label: "Details"
472
+ }, {
473
+ value: 'html',
474
+ label: "Page as HTML source"
475
+ }, {
476
+ value: 'schema',
477
+ label: "Page as HAXElementSchema"
478
+ }, {
479
+ value: 'md',
480
+ label: "Page as Markdown"
481
+ }];
482
+ if (search) {
483
+ for (const op of obj) {
484
+ if (op.value === search) {
485
+ return true;
486
+ }
487
+ }
488
+ return false;
489
+ }
490
+ return obj;
491
+ }
373
492
  function siteNodeOperations(search = null) {
374
493
  let obj = [{
375
494
  value: 'title',
@@ -410,6 +529,30 @@ function siteNodeOperations(search = null) {
410
529
  return obj;
411
530
  }
412
531
 
532
+ // broker a call to the open-api repo which is an express based wrapper for vercel (originally)
533
+ // this ensures the calls are identical and yet are converted to something the CLI can leverage
534
+ async function openApiBroker(call, body) {
535
+ let mfItem = _microFrontendRegistry.MicroFrontendRegistry.get(`@haxcms/${call}`);
536
+ // ensure we have a MFR record to do the connection
537
+ // fun thing is this is local file access directly via import()
538
+ if (mfItem) {
539
+ // dynamic import... this might upset some stuff later bc it's not a direct reference
540
+ // but it's working locally at least.
541
+ const handler = await (specifier => new Promise(r => r(specifier)).then(s => _interopRequireWildcard(require(s))))(`${mfItem.endpoint.replace('/api/', '/dist/')}.js`);
542
+ let res = new Res();
543
+ let req = {
544
+ body: JSON.stringify(body),
545
+ method: "post"
546
+ };
547
+ // js pass by ref for the win; these will both update bc of how we structured the calls
548
+ await handler.default(req, res);
549
+ // they'll need unpacked but that's a small price!
550
+ return {
551
+ req: req,
552
+ res: res
553
+ };
554
+ }
555
+ }
413
556
  // process site creation
414
557
  async function siteProcess(commandRun, project, port = '3000') {
415
558
  // auto select operations to perform if requested
@@ -436,10 +579,38 @@ async function siteProcess(commandRun, project, port = '3000') {
436
579
  "icon": "av:library-add"
437
580
  }
438
581
  };
582
+ // allow for importSite option
583
+ if (commandRun.options.importSite) {
584
+ if (!commandRun.options.importStructure) {
585
+ // assume hax to hax if it's not defined
586
+ commandRun.options.importStructure = 'haxcmsToSite';
587
+ }
588
+ // verify this is a valid way to do an import
589
+ if (commandRun.options.importStructure && _microFrontendRegistry.MicroFrontendRegistry.get(`@haxcms/${commandRun.options.importStructure}`)) {
590
+ let resp = await openApiBroker(commandRun.options.importStructure, {
591
+ repoUrl: commandRun.options.importSite
592
+ });
593
+ if (resp.res.data && resp.res.data.data && resp.res.data.data.items) {
594
+ siteRequest.build.structure = 'import';
595
+ siteRequest.build.items = resp.res.data.data.items;
596
+ }
597
+ if (resp.res.data && resp.res.data.data && resp.res.data.data.files) {
598
+ siteRequest.build.files = resp.res.data.data.files;
599
+ }
600
+ }
601
+ }
439
602
  HAXCMS.cliWritePath = `${project.path}`;
603
+ let res = new Res();
440
604
  await hax.RoutesMap.post.createSite({
441
605
  body: siteRequest
442
- }, fakeSend);
606
+ }, res);
607
+ if (commandRun.options.v) {
608
+ if (commandRun.options.format === 'yaml') {
609
+ console.log((0, _jsYaml.dump)(res.data));
610
+ } else {
611
+ console.log(res.data);
612
+ }
613
+ }
443
614
  s.stop((0, _statements.merlinSays)(`${project.name} created!`));
444
615
  await (0, _promises.setTimeout)(500);
445
616
  if (project.gitRepo && !commandRun.options.isMonorepo) {
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
7
  exports.webcomponentCommandDetected = webcomponentCommandDetected;
8
+ exports.webcomponentGenerateHAXSchema = webcomponentGenerateHAXSchema;
8
9
  exports.webcomponentProcess = webcomponentProcess;
9
10
  var fs = _interopRequireWildcard(require("node:fs"));
10
11
  var _promises = require("node:timers/promises");
@@ -27,6 +28,110 @@ exec('git --version', error => {
27
28
  sysGit = false;
28
29
  }
29
30
  });
31
+ class HAXWiring {
32
+ /**
33
+ * Return a haxProperties prototype / example structure
34
+ */
35
+ prototypeHaxProperties = () => {
36
+ // example properties valid for HAX context menu.
37
+ let props = {
38
+ api: "1",
39
+ type: "element",
40
+ editingElement: "core",
41
+ hideDefaultSettings: false,
42
+ canScale: true,
43
+ canEditSource: true,
44
+ contentEditable: false,
45
+ gizmo: {
46
+ title: "Tag name",
47
+ description: "",
48
+ icon: "icons:android",
49
+ color: "purple",
50
+ tags: ["Other"],
51
+ handles: [{
52
+ type: "data",
53
+ type_exclusive: false,
54
+ url: "src"
55
+ }],
56
+ meta: {
57
+ author: "auto"
58
+ },
59
+ requiresChildren: false,
60
+ requiresParent: false
61
+ },
62
+ settings: {
63
+ configure: [{
64
+ slot: "",
65
+ title: "Inner content",
66
+ description: "The slotted content that lives inside the tag",
67
+ inputMethod: "textfield",
68
+ icon: "android",
69
+ required: true,
70
+ validationType: "text"
71
+ }, {
72
+ slot: "button",
73
+ title: "Button content",
74
+ description: "The content that can override the button",
75
+ inputMethod: "textfield",
76
+ icon: "android",
77
+ required: true,
78
+ validationType: "text"
79
+ }, {
80
+ property: "title",
81
+ title: "Title",
82
+ description: "",
83
+ inputMethod: "textfield",
84
+ icon: "android",
85
+ required: true,
86
+ validationType: "text"
87
+ }, {
88
+ property: "primaryColor",
89
+ title: "Title",
90
+ description: "",
91
+ inputMethod: "textfield",
92
+ icon: "android",
93
+ required: false,
94
+ validation: ".*",
95
+ validationType: "text"
96
+ }],
97
+ advanced: [{
98
+ property: "secondaryColor",
99
+ title: "Secondary color",
100
+ description: "An optional secondary color used in certain edge cases.",
101
+ inputMethod: "colorpicker",
102
+ icon: "color"
103
+ }, {
104
+ property: "endPoint",
105
+ title: "API endpoint",
106
+ description: "An optional endpoint to hit and load in more data dymaically.",
107
+ inputMethod: "textfield",
108
+ icon: "android",
109
+ validation: "[a-z0-9]",
110
+ validationType: "url"
111
+ }],
112
+ developer: []
113
+ },
114
+ saveOptions: {
115
+ wipeSlot: false,
116
+ unsetAttributes: ["end-point", "secondary-color"]
117
+ },
118
+ documentation: {
119
+ howTo: "https://haxtheweb.org/welcome",
120
+ purpose: "https://haxtheweb.org/welcome"
121
+ },
122
+ demoSchema: [{
123
+ tag: "my-tag",
124
+ content: "<p>inner html</p>",
125
+ properties: {
126
+ endPoint: "https://cdn2.thecatapi.com/images/9j5.jpg",
127
+ primaryColor: "yellow",
128
+ title: "A cat"
129
+ }
130
+ }]
131
+ };
132
+ return props;
133
+ };
134
+ }
30
135
 
31
136
  // processing an element
32
137
  async function webcomponentProcess(commandRun, project, port = "8000") {
@@ -117,7 +222,7 @@ async function webcomponentProcess(commandRun, project, port = "8000") {
117
222
  await exec(`cd ${project.path}/${project.name} && ${commandRun.options.npmClient} install`);
118
223
  }
119
224
  } catch (e) {
120
- console.log(e);
225
+ console.warn(e);
121
226
  }
122
227
  s.stop((0, _statements.merlinSays)(`Everything is installed. It's go time`));
123
228
  }
@@ -155,8 +260,12 @@ ${_picocolors.default.underline(_picocolors.default.cyan(`http://localhost:${por
155
260
  // autodetect webcomponent
156
261
  async function webcomponentCommandDetected(commandRun, packageData = {}, port = "8000") {
157
262
  p.intro(`${_picocolors.default.bgBlack(_picocolors.default.white(` HAXTheWeb : Webcomponent detected `))}`);
158
- p.intro(`${_picocolors.default.bgBlue(_picocolors.default.white(` Name: ${packageData.name} `))}`);
159
- p.note(`${(0, _statements.merlinSays)(`I have summoned a sub-process daemon 👹`)}
263
+ p.intro(`${_picocolors.default.bgBlue(_picocolors.default.white(` Web component name: ${packageData.name} `))}`);
264
+ // if we support customElement analyzer (hax wcs do) then generate if asked
265
+ if (commandRun.options.writeHaxProperties && packageData.customElements) {
266
+ webcomponentGenerateHAXSchema(commandRun, packageData);
267
+ } else {
268
+ p.note(`${(0, _statements.merlinSays)(`I have summoned a sub-process daemon 👹`)}
160
269
 
161
270
  🚀 Running your ${_picocolors.default.bold('webcomponent')} ${_picocolors.default.bold(packageData.name)}:
162
271
  ${_picocolors.default.underline(_picocolors.default.cyan(`http://localhost:${port}`))}
@@ -169,16 +278,111 @@ async function webcomponentCommandDetected(commandRun, packageData = {}, port =
169
278
 
170
279
  ⌨️ To exit 🧙 Merlin press: ${_picocolors.default.bold(_picocolors.default.black(_picocolors.default.bgRed(` CTRL + C `)))}
171
280
  `);
172
- try {
173
- // ensure it's installed first, unless it's a monorepo
174
- if (!commandRun.options.isMonorepo) {
175
- let s = p.spinner();
176
- s.start((0, _statements.merlinSays)(`Installation magic (${commandRun.options.npmClient} install)`));
177
- await exec(`${commandRun.options.npmClient} install`);
178
- s.stop((0, _statements.merlinSays)(`Everything is installed. It's go time`));
281
+ try {
282
+ // ensure it's installed first, unless it's a monorepo. basic check for node_modules
283
+ // folder as far as if already installed so we don't double install needlessly
284
+ if (!commandRun.options.isMonorepo && !fs.existsSync("./node_modules")) {
285
+ let s = p.spinner();
286
+ s.start((0, _statements.merlinSays)(`Installation magic (${commandRun.options.npmClient} install)`));
287
+ await exec(`${commandRun.options.npmClient} install`);
288
+ s.stop((0, _statements.merlinSays)(`Everything is installed. It's go time`));
289
+ }
290
+ await exec(`${commandRun.options.npmClient} start`);
291
+ } catch (e) {
292
+ // don't log bc output is odd
179
293
  }
180
- await exec(`${commandRun.options.npmClient} start`);
181
- } catch (e) {
182
- // don't log bc output is odd
183
294
  }
295
+ }
296
+
297
+ // merge the web component factory libraries the user has installed
298
+ async function webcomponentGenerateHAXSchema(commandRun, packageData) {
299
+ // run analyzer automatically if we have it so that it's up to date
300
+ if (packageData.scripts.analyze) {
301
+ await exec(`${commandRun.options.npmClient} run analyze`);
302
+ }
303
+ if (fs.existsSync(`${process.cwd()}/${packageData.customElements}`)) {
304
+ const ceFileData = fs.readFileSync(`${process.cwd()}/${packageData.customElements}`, 'utf8', (error, data) => {
305
+ if (error) {
306
+ console.warn(error);
307
+ return;
308
+ }
309
+ return data;
310
+ });
311
+ let wiring = new HAXWiring();
312
+ if (commandRun.options.debug) {
313
+ console.log(ceFileData);
314
+ }
315
+ if (ceFileData) {
316
+ let ce = JSON.parse(ceFileData);
317
+ await ce.modules.forEach(async modules => {
318
+ await modules.declarations.forEach(async declarations => {
319
+ let props = wiring.prototypeHaxProperties();
320
+ props.gizmo.title = declarations.tagName.replace('-', ' ');
321
+ props.gizmo.tags = ["Other"];
322
+ props.gizmo.handles = [];
323
+ props.gizmo.meta.author = "HAXTheWeb core team";
324
+ delete props.gizmo.shortcutKey;
325
+ delete props.gizmo.requiresChildren;
326
+ delete props.gizmo.requiresParent;
327
+ props.settings.configure = [];
328
+ props.settings.advanced = [];
329
+ props.documentation = {
330
+ howTo: null,
331
+ purpose: null
332
+ };
333
+ props.saveOptions = {
334
+ unsetAttributes: []
335
+ };
336
+ props.demoSchema = [{
337
+ tag: declarations.tagName,
338
+ content: "",
339
+ properties: {}
340
+ }];
341
+ let propData = [];
342
+ if (declarations.attributes) {
343
+ propData = declarations.attributes;
344
+ }
345
+ // loop through and if props are things we can map then do it
346
+ await propData.forEach(async prop => {
347
+ if (["t", "colors", '_haxState', "elementVisible"].includes(prop.fieldName)) {
348
+ props.saveOptions.unsetAttributes.push(prop.fieldName);
349
+ } else {
350
+ let type = "textfield";
351
+ if (prop.type && prop.type.text) {
352
+ type = getInputMethodFromType(prop.type.text);
353
+ }
354
+ if (type) {
355
+ let propSchema = {
356
+ property: prop.fieldName,
357
+ title: prop.name,
358
+ description: "",
359
+ inputMethod: type
360
+ };
361
+ if (prop.default !== undefined) {
362
+ props.demoSchema[0].properties[prop.fieldName] = prop.default;
363
+ }
364
+ props.settings.configure.push(propSchema);
365
+ }
366
+ }
367
+ });
368
+ if (commandRun.options.v) {
369
+ console.log(JSON.stringify(props, null, 2));
370
+ }
371
+ fs.writeFileSync(`./lib/${declarations.tagName}.haxProperties.json`, JSON.stringify(props, null, 2));
372
+ console.log(`schema written to: ./lib/${declarations.tagName}.haxProperties.json`);
373
+ });
374
+ });
375
+ }
376
+ }
377
+ }
378
+ function getInputMethodFromType(type) {
379
+ switch (type) {
380
+ case "string":
381
+ return "textfield";
382
+ case "number":
383
+ return "number";
384
+ case "boolean":
385
+ return "boolean";
386
+ }
387
+ return false;
184
388
  }
@@ -33,7 +33,7 @@
33
33
  },
34
34
  "devDependencies": {
35
35
  "@babel/preset-env": "^7.16.4",
36
- "@custom-elements-manifest/analyzer": "^0.4.17",
36
+ "@custom-elements-manifest/analyzer": "^0.10.3",
37
37
  "@open-wc/building-rollup": "^3.0.2",
38
38
  "@rollup/plugin-babel": "^6.0.4",
39
39
  "@rollup/plugin-node-resolve": "^15.2.3",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haxtheweb/create",
3
- "version": "9.0.10",
3
+ "version": "9.0.12",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -42,19 +42,22 @@
42
42
  "dependencies": {
43
43
  "@clack/core": "0.3.4",
44
44
  "@clack/prompts": "0.7.0",
45
- "@haxtheweb/haxcms-nodejs": "^9.0.15",
45
+ "@haxtheweb/haxcms-nodejs": "^9.0.17",
46
+ "@haxtheweb/open-apis": "^9.0.11",
47
+ "commander": "12.1.0",
48
+ "node-html-parser": "6.1.13",
46
49
  "ejs": "3.1.10",
47
- "picocolors": "1.0.1",
48
- "commander": "12.1.0"
50
+ "js-yaml": "4.1.0",
51
+ "picocolors": "1.0.1"
49
52
  },
50
53
  "devDependencies": {
51
- "nodemon": "^3.1.7",
52
54
  "@babel/cli": "^7.24.6",
53
55
  "@babel/core": "^7.24.6",
54
56
  "@babel/preset-env": "7.24.6",
55
57
  "@babel/register": "^7.24.6",
56
- "@custom-elements-manifest/analyzer": "^0.10.2",
58
+ "@custom-elements-manifest/analyzer": "^0.10.3",
57
59
  "babel-plugin-transform-dynamic-import": "^2.1.0",
58
- "commit-and-tag-version": "12.4.1"
60
+ "commit-and-tag-version": "12.4.1",
61
+ "nodemon": "^3.1.7"
59
62
  }
60
63
  }