@haxtheweb/create 9.0.11 → 9.0.13

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/README.md CHANGED
@@ -1,10 +1,17 @@
1
+ [![License: Apache 2.0](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
2
+ [![Lit](https://img.shields.io/badge/-Lit-324fff?style=flat&logo=data:image/svg%2bxml;base64,PHN2ZyBmaWxsPSIjZmZmIiB2aWV3Qm94PSIwIDAgMTYwIDIwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJtMTYwIDgwdjgwbC00MC00MHptLTQwIDQwdjgwbDQwLTQwem0wLTgwdjgwbC00MC00MHptLTQwIDQwdjgwbDQwLTQwem0tNDAtNDB2ODBsNDAtNDB6bTQwLTQwdjgwbC00MC00MHptLTQwIDEyMHY4MGwtNDAtNDB6bS00MC00MHY4MGw0MC00MHoiLz48L3N2Zz4%3D)](https://lit.dev/)
3
+ [![#HAXTheWeb](https://img.shields.io/badge/-HAXTheWeb-999999FF?style=flat&logo=data:image/svg%2bxml;base64,PHN2ZyBpZD0iZmVhMTExZTAtMjEwZC00Y2QwLWJhMWQtZGZmOTQyODc0Njg1IiBkYXRhLW5hbWU9IkxheWVyIDEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDE4NC40IDEzNS45NyI+PGRlZnM+PHN0eWxlPi5lMWJjMjAyNS0xODAwLTRkYzItODc4NS1jNDZlZDEwM2Y0OTJ7ZmlsbDojMjMxZjIwO308L3N0eWxlPjwvZGVmcz48cGF0aCBjbGFzcz0iZTFiYzIwMjUtMTgwMC00ZGMyLTg3ODUtYzQ2ZWQxMDNmNDkyIiBkPSJNNzguMDcsODMuNDVWNTVIODYuMnY4LjEzaDE2LjI2djQuMDdoNC4wN1Y4My40NUg5OC40VjY3LjE5SDg2LjJWODMuNDVaIi8+PHBvbHlnb24gcG9pbnRzPSIxNTMuMTMgNjMuNyAxNTMuMTMgNTEuMzkgMTQwLjU0IDUxLjM5IDE0MC41NCAzOS4wOSAxMjcuOTUgMzkuMDkgMTI3Ljk1IDI2Ljc5IDEwMi43OCAyNi43OSAxMDIuNzggMzkuMDkgMTE1LjM2IDM5LjA5IDExNS4zNiA1MS4zOSAxMjcuOTUgNTEuMzkgMTI3Ljk1IDYzLjcgMTQwLjU0IDYzLjcgMTQwLjU0IDc2IDEyNy4zNiA3NiAxMjcuMzYgODguMyAxMTQuNzggODguMyAxMTQuNzggMTAwLjYxIDEwMi4xOSAxMDAuNjEgMTAyLjE5IDExMi45MSAxMjcuMzYgMTEyLjkxIDEyNy4zNiAxMDAuNjEgMTM5Ljk1IDEwMC42MSAxMzkuOTUgODguMyAxNTIuNTQgODguMyAxNTIuNTQgNzYgMTY1LjcyIDc2IDE2NS43MiA2My43IDE1My4xMyA2My43Ii8+PHBvbHlnb24gcG9pbnRzPSIzMy4xMyA2My43IDMzLjEzIDUxLjM5IDQ1LjcyIDUxLjM5IDQ1LjcyIDM5LjA5IDU4LjMxIDM5LjA5IDU4LjMxIDI2Ljc5IDgzLjQ4IDI2Ljc5IDgzLjQ4IDM5LjA5IDcwLjg5IDM5LjA5IDcwLjg5IDUxLjM5IDU4LjMxIDUxLjM5IDU4LjMxIDYzLjcgNDUuNzIgNjMuNyA0NS43MiA3NiA1OC44OSA3NiA1OC44OSA4OC4zIDcxLjQ4IDg4LjMgNzEuNDggMTAwLjYxIDg0LjA3IDEwMC42MSA4NC4wNyAxMTIuOTEgNTguODkgMTEyLjkxIDU4Ljg5IDEwMC42MSA0Ni4zMSAxMDAuNjEgNDYuMzEgODguMyAzMy43MiA4OC4zIDMzLjcyIDc2IDIwLjU0IDc2IDIwLjU0IDYzLjcgMzMuMTMgNjMuNyIvPjwvc3ZnPg==)](https://haxtheweb.org/)
4
+ [![Published on npm](https://img.shields.io/npm/v/@haxtheweb/create?style=flat)](https://www.npmjs.com/package/@haxtheweb/create)
5
+ [![build](https://github.com/haxtheweb/create/workflows/build/badge.svg)](https://github.com/haxtheweb/create/actions)
6
+ [![X](https://img.shields.io/twitter/follow/haxtheweb.svg?style=social&label=Follow)](https://twitter.com/intent/follow?screen_name=haxtheweb)
7
+
1
8
  # HAX The CLI
2
9
  Rapidly build web components for the Web that work with HAX. HAX The Web's CLI tools empower you to rapidly..
3
10
 
4
11
  ```bash
5
12
  # this allows you to then use hax command
6
13
  npm install @haxtheweb/create --global
7
- # then run
14
+ # then run this for interactive prompt
8
15
  hax start
9
16
  ```
10
17
 
@@ -12,11 +19,12 @@ hax start
12
19
 
13
20
  ## Default / global / new context
14
21
  - `hax start` - fun ascii art and interactive CLI (via [clack](https://www.clack.cc/) )
15
- - `hax --name my-element --y` - Make a new HAX capable, i18n wired, Design system (DDD) driven web component
22
+ - `hax webcomponent my-element --y` - Make a new HAX capable, i18n wired, Design system (DDD) driven web component
16
23
  - if in a monorepo root, will place in correct location / inherit settings
17
24
  - `hax site mysite --y` - create a new HAXsite (HAXcms, single site)
18
25
 
19
26
  ## --help
27
+ Run `hax help` or `hax webcomponent --help` or `hax site --help` for up-to-date listing
20
28
  ```
21
29
  Usage: hax [options] [command]
22
30
 
@@ -70,12 +78,8 @@ Commands:
70
78
  npx @haxtheweb/create
71
79
  # this is same as above, better windows CLI support
72
80
  npm init @haxtheweb
73
- ```
74
-
75
- ## Usage in other programs
76
- https://stackoverflow.com/questions/69208298/use-node-bins-without-installing-the-package-globally explains it but you should be able to use the CLI as part of another project as follows:
77
- ```json
78
- {
81
+ - Try Hax: https://hax.cloud
82
+ - HAXCellence https://haxtheweb.org/what-is-hax
79
83
  "scripts": {
80
84
  "hax": "hax"
81
85
  }
@@ -109,3 +113,25 @@ Build a HAX site that can be published and transported anywhere. Your users migh
109
113
  - Ability to import via URL just like the front-end
110
114
  - Theme development starting point to be able to build themes locally
111
115
  - Primed to publish to gh-pages, vercel and more
116
+
117
+ # Get Help / Issues / Support
118
+ - Discord Channel - https://bit.ly/hax-discord
119
+ - Unified issue queue - https://github.com/haxtheweb/issues/issues
120
+ - Using Merlin directly in any HAX spaces and type "Issue" to jump start a report!
121
+
122
+ ## Watch and Learn more about HAX here:
123
+ - Try Hax: https://hax.cloud
124
+ - HAXCellence https://haxtheweb.org/what-is-hax
125
+ - Youtube channel - https://www.youtube.com/@haxtheweb
126
+
127
+ # Related links and tech
128
+ - [NPM Package list](https://www.npmjs.com/org/haxtheweb)
129
+ - [HAXcms (NodeJS)](https://github.com/haxtheweb/haxcms-nodejs)
130
+ - [HAXcms (PHP)](https://github.com/haxtheweb/haxcms-php)
131
+ - [Storybook docs](https://open-apis.hax.cloud/)
132
+ - [HAX [dot] PSU](https://hax.psu.edu)
133
+ - [HAX doc site](https://haxtheweb.org/)
134
+ - [HAX + 11ty](https://github.com/haxtheweb/hax11ty)
135
+
136
+
137
+ ![HAX Traveler: World Changer](https://raw.githubusercontent.com/haxtheweb/art/refs/heads/main/haxtheweb/hax-traveler-world-changer.jpg)
package/dist/create.js CHANGED
@@ -28,10 +28,10 @@ exec('git --version', error => {
28
28
  });
29
29
  async function main() {
30
30
  var commandRun = {};
31
- _commander.program.option('--').option('--v', 'Verbose output for developers').option('--path <char>', 'where to perform operation').option('--npm-client <char>', 'npm client to use (must be installed) npm, yarn, pnpm', 'npm').option('--y', 'yes to all questions').option('--skip', 'skip frills like animations').option('--auto', 'yes to all questions, alias of y')
31
+ _commander.program.option('--').option('--v', 'Verbose output').option('--debug', 'Output for developers').option('--format <char>', 'Output format; json (default), yaml').option('--path <char>', 'where to perform operation').option('--npm-client <char>', 'npm client to use (must be installed) npm, yarn, pnpm', 'npm').option('--y', 'yes to all questions').option('--skip', 'skip frills like animations').option('--auto', 'yes to all questions, alias of y').option('--no-i', 'prevent interactions / sub-process, good for scripting')
32
32
 
33
33
  // options for webcomponent
34
- .option('--org <char>', 'organization for package.json').option('--author <char>', 'author for site / package.json')
34
+ .option('--org <char>', 'organization for package.json').option('--author <char>', 'author for site / package.json').option('--writeHaxProperties', 'Write haxProperties for the element')
35
35
 
36
36
  // options for site
37
37
  .option('--import-site <char>', 'URL of site to import').option('--import-structure <char>', `import method to use:\n\rpressbooksToSite\n\relmslnToSite\n\rhaxcmsToSite\n\rnotionToSite\n\rgitbookToSite\n\revolutionToSite\n\rhtmlToSite\n\rdocxToSite`).option('--node-op <char>', 'node operation to perform').option('--item-id <char>', 'node ID to operate on').option('--name <char>', 'name of the project').option('--domain <char>', 'published domain name').helpCommand(true);
@@ -61,7 +61,7 @@ async function main() {
61
61
  commandRun.arguments.action = action;
62
62
  commandRun.options.skip = true;
63
63
  }
64
- }).option('--path <char>', 'path the project should be created in').option('--import-site <char>', 'URL of site to import').option('--import-structure <char>', `import method to use:\n\rpressbooksToSite\n\relmslnToSite\n\rhaxcmsToSite\n\rnotionToSite\n\rgitbookToSite\n\revolutionToSite\n\rhtmlToSite\n\rdocxToSite`).option('--name <char>', 'name of the site (when creating a new one)').option('--domain <char>', 'published domain name').option('--node-op <char>', 'node operation to perform').version(await HAXCMS.getHAXCMSVersion());
64
+ }).option('--path <char>', 'path the project should be created in').option('--import-site <char>', 'URL of site to import').option('--import-structure <char>', `import method to use:\n\rpressbooksToSite\n\relmslnToSite\n\rhaxcmsToSite\n\rnotionToSite\n\rgitbookToSite\n\revolutionToSite\n\rhtmlToSite\n\rdocxToSite`).option('--name <char>', 'name of the site (when creating a new one)').option('--domain <char>', 'published domain name').option('--node-op <char>', 'node operation to perform').option('--no-i', 'prevent interactions / sub-process, good for scripting').version(await HAXCMS.getHAXCMSVersion());
65
65
  let siteNodeOps = (0, _site.siteNodeOperations)();
66
66
  for (var i in siteNodeOps) {
67
67
  _commander.program.option(`--${(0, _utils.camelToDash)(siteNodeOps[i].value)} <char>`, `${siteNodeOps[i].label}`);
@@ -80,14 +80,14 @@ async function main() {
80
80
  commandRun.arguments.name = name;
81
81
  commandRun.options.skip = true;
82
82
  }
83
- }).option('--path <char>', 'path the project should be created in').option('--org <char>', 'organization for package.json').option('--author <char>', 'author for site / package.json');
83
+ }).option('--path <char>', 'path the project should be created in').option('--org <char>', 'organization for package.json').option('--author <char>', 'author for site / package.json').option('--writeHaxProperties', 'Write haxProperties for the element').option('--no-i', 'prevent interactions / sub-process, good for scripting');
84
84
  // process program arguments
85
85
  _commander.program.parse();
86
86
  commandRun.options = {
87
87
  ...commandRun.options,
88
88
  ..._commander.program.opts()
89
89
  };
90
- if (commandRun.options.v) {
90
+ if (commandRun.options.debug) {
91
91
  console.log(commandRun);
92
92
  }
93
93
  // auto and y assume same thing
@@ -109,9 +109,14 @@ async function main() {
109
109
  if (!commandRun.options.path && commandRun.options.skip) {
110
110
  commandRun.options.path = process.cwd();
111
111
  }
112
- if (commandRun.options.auto) {
113
- commandRun.options.org = '';
114
- commandRun.options.author = author;
112
+ // if we skip stuff then set org/author automatically
113
+ if (commandRun.options.skip || commandRun.options.auto) {
114
+ if (!commandRun.options.org) {
115
+ commandRun.options.org = '';
116
+ }
117
+ if (!commandRun.options.author) {
118
+ commandRun.options.author = author;
119
+ }
115
120
  }
116
121
  let packageData = {};
117
122
  let testPackages = [path.join(process.cwd(), 'package.json'), path.join(process.cwd(), '../', 'package.json'), path.join(process.cwd(), '../', '../', 'package.json')];
@@ -121,7 +126,10 @@ async function main() {
121
126
  let packLoc = testPackages.shift();
122
127
  if (fs.existsSync(packLoc)) {
123
128
  try {
124
- packageData = JSON.parse(fs.readFileSync(`${process.cwd()}/package.json`));
129
+ packageData = {
130
+ ...JSON.parse(fs.readFileSync(packLoc)),
131
+ ...packageData
132
+ };
125
133
  // assume we are working on a web component / existing if we find this key
126
134
  if (packageData.hax && packageData.hax.cli) {
127
135
  commandRun.program = 'webcomponent';
@@ -129,6 +137,8 @@ async function main() {
129
137
  // leverage these values if they exist downstream
130
138
  if (packageData.npmClient) {
131
139
  commandRun.options.npmClient = packageData.npmClient;
140
+ } else {
141
+ commandRun.options.npmClient = 'npm';
132
142
  }
133
143
  // see if we're in a monorepo
134
144
  if (packageData.useWorkspaces && packageData.workspaces && packageData.workspaces.packages && packageData.workspaces.packages[0]) {
@@ -149,12 +159,15 @@ async function main() {
149
159
  }
150
160
  }
151
161
  }
162
+ if (commandRun.options.debug) {
163
+ console.log(packageData);
164
+ }
152
165
  // CLI works within context of the site if one is detected, otherwise we can do other thingss
153
166
  if (await hax.systemStructureContext()) {
154
167
  commandRun.program = 'site';
155
168
  commandRun.options.skip = true;
156
169
  await (0, _site.siteCommandDetected)(commandRun);
157
- } else if (packageData && packageData.hax && packageData.hax.cli && packageData.scripts.start) {
170
+ } else if (packageData && (packageData.customElements || packageData.hax && packageData.hax.cli) && packageData.scripts.start) {
158
171
  commandRun.program = 'webcomponent';
159
172
  commandRun.options.skip = true;
160
173
  await (0, _webcomponent.webcomponentCommandDetected)(commandRun, packageData);
@@ -185,7 +198,7 @@ async function main() {
185
198
  project = {
186
199
  type: commandRun.command
187
200
  };
188
- } else {
201
+ } else if (commandRun.options.i) {
189
202
  project = await p.group({
190
203
  type: ({
191
204
  results
@@ -198,7 +211,7 @@ async function main() {
198
211
  label: '🏗️ Create a Web Component'
199
212
  }, {
200
213
  value: 'site',
201
- label: '🏡 Create a HAXcms site (single)'
214
+ label: '🏡 Create a HAXsite'
202
215
  }, {
203
216
  value: 'quit',
204
217
  label: '🚪 Quit'
@@ -211,6 +224,8 @@ async function main() {
211
224
  process.exit(0);
212
225
  }
213
226
  });
227
+ } else if (!commandRun.options.i) {
228
+ process.exit(0);
214
229
  }
215
230
  // detect being in a haxcms scaffold. easiest way is _sites being in this directory
216
231
  // set the path automatically so we skip the question
@@ -321,7 +336,7 @@ async function main() {
321
336
  extras: ({
322
337
  results
323
338
  }) => {
324
- if (!commandRun.options.auto) {
339
+ if (!commandRun.options.auto && commandRun.options.i) {
325
340
  let options = [];
326
341
  let initialValues = [];
327
342
  if (commandRun.command === "webcomponent" || results.type === "webcomponent") {
@@ -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 _utils = require("../utils.js");
17
21
  var _microFrontendRegistry = require("../micro-frontend-registry.js");
18
22
  var haxcmsNodejsCli = _interopRequireWildcard(require("@haxtheweb/haxcms-nodejs/dist/cli.js"));
19
23
  var hax = _interopRequireWildcard(require("@haxtheweb/haxcms-nodejs"));
@@ -27,7 +31,7 @@ globalThis.MicroFrontendRegistryConfig = {
27
31
  base: `@haxtheweb/open-apis/`
28
32
  };
29
33
  // emable HAXcms routes so we have name => path just like on frontend!
30
- _microFrontendRegistry.MicroFrontendRegistry.enableServices(['haxcms']);
34
+ _microFrontendRegistry.MicroFrontendRegistry.enableServices(['core', 'haxcms', 'experimental']);
31
35
  const HAXCMS = hax.HAXCMS;
32
36
  const exec = util.promisify(child_process.exec);
33
37
  var sysSurge = true;
@@ -36,32 +40,65 @@ exec('surge --version', error => {
36
40
  sysSurge = false;
37
41
  }
38
42
  });
39
- const fakeSend = {
40
- send: json => console.log(json),
41
- sendStatus: data => console.log(data)
42
- };
43
+
44
+ // fake response class so we can capture the response from the headless route as opposed to print to console
45
+ // this way we can handle as data or if use is requesting output format to change we can respond
46
+ class Res {
47
+ constructor() {
48
+ this.query = {};
49
+ this.data = null;
50
+ this.statusCode = null;
51
+ }
52
+ send(data) {
53
+ this.data = data;
54
+ return this;
55
+ }
56
+ status(status) {
57
+ this.statusCode = status;
58
+ return this;
59
+ }
60
+ json(data) {
61
+ this.data = JSON.parse(JSON.stringify(data));
62
+ return this;
63
+ }
64
+ setHeader() {
65
+ return this;
66
+ }
67
+ }
43
68
  function siteActions() {
44
69
  return [{
45
70
  value: 'start',
46
- label: "Start site (http://localhost)"
71
+ label: "Launch site (http://localhost)"
47
72
  }, {
48
- value: 'status',
49
- label: "Status"
50
- }, {
51
- value: 'sync',
52
- label: "Sync git"
53
- }, {
54
- value: 'theme',
55
- label: "Change theme"
73
+ value: 'node:stats',
74
+ label: "Node: Stats"
56
75
  }, {
57
76
  value: 'node:add',
58
- label: "Add page"
77
+ label: "Node: Add page"
59
78
  }, {
60
79
  value: 'node:edit',
61
- label: "Edit page"
80
+ label: "Node: Edit page"
62
81
  }, {
63
82
  value: 'node:delete',
64
- label: "Delete page"
83
+ label: "Node: Delete page"
84
+ }, {
85
+ value: 'status',
86
+ label: "Site: Status"
87
+ }, {
88
+ value: 'theme',
89
+ label: "Site: Change theme"
90
+ }, {
91
+ value: 'file:list',
92
+ label: "Site: List files"
93
+ }, {
94
+ value: 'site:html',
95
+ label: "Site: Full site as HTML"
96
+ }, {
97
+ value: 'site:md',
98
+ label: "Site: Full site as Markdown"
99
+ }, {
100
+ value: 'sync',
101
+ label: "Site: Sync git"
65
102
  }];
66
103
  }
67
104
  async function siteCommandDetected(commandRun) {
@@ -138,6 +175,67 @@ async function siteCommandDetected(commandRun) {
138
175
  console.log(e.stderr);
139
176
  }
140
177
  break;
178
+ case "node:stats":
179
+ try {
180
+ if (!commandRun.options.itemId) {
181
+ commandRun.options.itemId = await p.select({
182
+ message: `Select an item to edit`,
183
+ required: true,
184
+ options: [{
185
+ value: null,
186
+ label: "-- edit nothing, exit --"
187
+ }, ...(await siteItemsOptionsList(activeHaxsite))]
188
+ });
189
+ }
190
+ if (commandRun.options.itemId) {
191
+ let nodeOps = siteNodeStatsOperations();
192
+ let page = activeHaxsite.loadNode(commandRun.options.itemId);
193
+ // select which aspect of this we are editing
194
+ if (!commandRun.options.nodeOp) {
195
+ commandRun.options.nodeOp = await p.select({
196
+ message: `${page.title} (${page.id}) - Node operations`,
197
+ required: true,
198
+ options: [{
199
+ value: null,
200
+ label: "-- Exit --"
201
+ }, ...nodeOps]
202
+ });
203
+ }
204
+ if (commandRun.options.nodeOp && siteNodeStatsOperations(commandRun.options.nodeOp)) {
205
+ switch (commandRun.options.nodeOp) {
206
+ case 'details':
207
+ console.log(page);
208
+ break;
209
+ case 'html':
210
+ console.log(await activeHaxsite.getPageContent(page));
211
+ break;
212
+ case 'schema':
213
+ // next up
214
+ let html = await activeHaxsite.getPageContent(page);
215
+ let dom = (0, _nodeHtmlParser.parse)(`<div id="fullpage">${html}</div>`);
216
+ let els = [];
217
+ for (var i in dom.querySelector('#fullpage').childNodes) {
218
+ let node = dom.querySelector('#fullpage').childNodes[i];
219
+ if (node && node.getAttribute) {
220
+ els.push(await nodeToHaxElement(node, null));
221
+ }
222
+ }
223
+ console.log(els);
224
+ break;
225
+ case 'md':
226
+ let resp = await openApiBroker('@core', 'htmlToMd', {
227
+ html: await activeHaxsite.getPageContent(page)
228
+ });
229
+ console.log(resp.res.data.data);
230
+ break;
231
+ }
232
+ }
233
+ }
234
+ } catch (e) {
235
+ console.log(e.stderr);
236
+ console.log(e);
237
+ }
238
+ break;
141
239
  case "node:add":
142
240
  try {
143
241
  // @todo accept title if not supplied
@@ -365,6 +463,42 @@ async function siteCommandDetected(commandRun) {
365
463
  console.log(e.stderr);
366
464
  }
367
465
  break;
466
+ case "file:list":
467
+ let res = new Res();
468
+ await hax.RoutesMap.get.listFiles({
469
+ query: activeHaxsite.name,
470
+ filename: commandRun.options.filename
471
+ }, res);
472
+ if (commandRun.options.format === 'yaml') {
473
+ console.log((0, _jsYaml.dump)(res.data));
474
+ } else {
475
+ console.log(res.data);
476
+ }
477
+ break;
478
+ case "site:html":
479
+ case "site:md":
480
+ let siteContent = '';
481
+ activeHaxsite = await hax.systemStructureContext();
482
+ let items = [];
483
+ if (commandRun.options.itemId != null) {
484
+ items = activeHaxsite.manifest.findBranch(commandRun.options.itemId);
485
+ } else {
486
+ items = activeHaxsite.manifest.orderTree(activeHaxsite.manifest.items);
487
+ }
488
+ for (var i in items) {
489
+ let page = activeHaxsite.loadNode(items[i].id);
490
+ siteContent += `<h1>${items[i].title}</h1>\n\r`;
491
+ siteContent += `<div data-jos-item-id="${items[i].id}">\n\r${await activeHaxsite.getPageContent(page)}\n\r</div>\n\r`;
492
+ }
493
+ if (operation.action === 'site:md') {
494
+ let resp = await openApiBroker('@core', 'htmlToMd', {
495
+ html: siteContent
496
+ });
497
+ console.log(resp.res.data.data);
498
+ } else {
499
+ console.log(siteContent);
500
+ }
501
+ break;
368
502
  case "quit":
369
503
  // quit
370
504
  process.exit(0);
@@ -377,6 +511,30 @@ async function siteCommandDetected(commandRun) {
377
511
  }
378
512
  (0, _statements.communityStatement)();
379
513
  }
514
+ function siteNodeStatsOperations(search = null) {
515
+ let obj = [{
516
+ value: 'details',
517
+ label: "Details"
518
+ }, {
519
+ value: 'html',
520
+ label: "Page as HTML source"
521
+ }, {
522
+ value: 'schema',
523
+ label: "Page as HAXElementSchema"
524
+ }, {
525
+ value: 'md',
526
+ label: "Page as Markdown"
527
+ }];
528
+ if (search) {
529
+ for (const op of obj) {
530
+ if (op.value === search) {
531
+ return true;
532
+ }
533
+ }
534
+ return false;
535
+ }
536
+ return obj;
537
+ }
380
538
  function siteNodeOperations(search = null) {
381
539
  let obj = [{
382
540
  value: 'title',
@@ -417,37 +575,16 @@ function siteNodeOperations(search = null) {
417
575
  return obj;
418
576
  }
419
577
 
420
- // fake response clas so we can capture the response from the headless route as opposed to print to console
421
- class Res {
422
- constructor() {
423
- this.query = {};
424
- this.data = null;
425
- this.statusCode = null;
426
- }
427
- send(data) {
428
- this.data = data;
429
- return this;
430
- }
431
- status(status) {
432
- this.statusCode = status;
433
- return this;
434
- }
435
- setHeader() {
436
- return this;
437
- }
438
- }
439
-
440
578
  // broker a call to the open-api repo which is an express based wrapper for vercel (originally)
441
579
  // this ensures the calls are identical and yet are converted to something the CLI can leverage
442
- async function openApiBroker(call, body) {
443
- let mfItem = _microFrontendRegistry.MicroFrontendRegistry.get(`@haxcms/${call}`);
580
+ async function openApiBroker(scope, call, body) {
581
+ let mfItem = _microFrontendRegistry.MicroFrontendRegistry.get(`${scope}/${call}`);
444
582
  // ensure we have a MFR record to do the connection
445
583
  // fun thing is this is local file access directly via import()
446
584
  if (mfItem) {
447
585
  // dynamic import... this might upset some stuff later bc it's not a direct reference
448
586
  // but it's working locally at least.
449
587
  const handler = await (specifier => new Promise(r => r(specifier)).then(s => _interopRequireWildcard(require(s))))(`${mfItem.endpoint.replace('/api/', '/dist/')}.js`);
450
- // start the fake response
451
588
  let res = new Res();
452
589
  let req = {
453
590
  body: JSON.stringify(body),
@@ -466,8 +603,10 @@ async function openApiBroker(call, body) {
466
603
  async function siteProcess(commandRun, project, port = '3000') {
467
604
  // auto select operations to perform if requested
468
605
  if (!project.extras) {
469
- let extras = ['launch'];
470
- project.extras = extras;
606
+ project.extras = [];
607
+ if (commandRun.options.i) {
608
+ project.extras = ['launch'];
609
+ }
471
610
  }
472
611
  let s = p.spinner();
473
612
  s.start((0, _statements.merlinSays)(`Creating new site: ${project.name}`));
@@ -496,7 +635,7 @@ async function siteProcess(commandRun, project, port = '3000') {
496
635
  }
497
636
  // verify this is a valid way to do an import
498
637
  if (commandRun.options.importStructure && _microFrontendRegistry.MicroFrontendRegistry.get(`@haxcms/${commandRun.options.importStructure}`)) {
499
- let resp = await openApiBroker(commandRun.options.importStructure, {
638
+ let resp = await openApiBroker('@haxcms', commandRun.options.importStructure, {
500
639
  repoUrl: commandRun.options.importSite
501
640
  });
502
641
  if (resp.res.data && resp.res.data.data && resp.res.data.data.items) {
@@ -509,9 +648,17 @@ async function siteProcess(commandRun, project, port = '3000') {
509
648
  }
510
649
  }
511
650
  HAXCMS.cliWritePath = `${project.path}`;
651
+ let res = new Res();
512
652
  await hax.RoutesMap.post.createSite({
513
653
  body: siteRequest
514
- }, fakeSend);
654
+ }, res);
655
+ if (commandRun.options.v) {
656
+ if (commandRun.options.format === 'yaml') {
657
+ console.log((0, _jsYaml.dump)(res.data));
658
+ } else {
659
+ console.log(res.data);
660
+ }
661
+ }
515
662
  s.stop((0, _statements.merlinSays)(`${project.name} created!`));
516
663
  await (0, _promises.setTimeout)(500);
517
664
  if (project.gitRepo && !commandRun.options.isMonorepo) {
@@ -586,4 +733,101 @@ async function siteThemeList() {
586
733
  });
587
734
  }
588
735
  return items;
736
+ }
737
+
738
+ // @fork of the hax core util for this so that we avoid api difference between real dom and parse nodejs dom
739
+ async function nodeToHaxElement(node, eventName = "insert-element") {
740
+ if (!node) {
741
+ return null;
742
+ }
743
+ // build out the properties to send along
744
+ var props = {};
745
+ // support basic styles
746
+ if (typeof node.getAttribute("style") !== typeof undefined) {
747
+ props.style = node.getAttribute("style");
748
+ }
749
+ // don't set a null style
750
+ if (props.style === null || props.style === "null") {
751
+ delete props.style;
752
+ }
753
+ // test if a class exists, not everything scopes
754
+ if (typeof node.getAttribute('class') !== typeof undefined) {
755
+ props.class = node.getAttribute('class').replace("hax-active", "");
756
+ }
757
+ // test if a id exists as its a special case in attributes... of course
758
+ if (typeof node.getAttribute('id') !== typeof undefined) {
759
+ props.id = node.getAttribute("id");
760
+ }
761
+ let tmpProps;
762
+ // weak fallback
763
+ if (typeof tmpProps === typeof undefined) {
764
+ tmpProps = node.__data;
765
+ }
766
+ // complex elements need complex support
767
+ if (typeof tmpProps !== typeof undefined) {
768
+ // run through attributes, though non-reflected props won't be here
769
+ // run through props, we always defer to property values
770
+ for (var property in tmpProps) {
771
+ // make sure we only set things that have a value
772
+ if (property != "class" && property != "style" && tmpProps.hasOwnProperty(property) && typeof node[property] !== undefined && node[property] != null && node[property] != "") {
773
+ props[property] = node[property];
774
+ }
775
+ // special support for false boolean
776
+ else if (node[property] === false) {
777
+ props[property] = false;
778
+ } else if (node[property] === true) {
779
+ props[property] = true;
780
+ } else if (node[property] === 0) {
781
+ props[property] = 0;
782
+ } else {
783
+ // unknown prop setting / ignored
784
+ //console.warn(node[property], property);
785
+ }
786
+ }
787
+ for (var attribute in node._attrs) {
788
+ // make sure we only set things that have a value
789
+ if (typeof node._attrs[attribute] !== typeof undefined && attribute != "class" && attribute != "style" && attribute != "id" && typeof node._attrs[attribute] !== undefined && node._attrs[attribute] != null && node._attrs[attribute] != "") {
790
+ props[attribute] = node._attrs[attribute];
791
+ } else if (node._attrs[attribute] == "0") {
792
+ props[attribute] = node._attrs[attribute];
793
+ } else {
794
+ // note: debug here if experiencing attributes that won't bind
795
+ }
796
+ }
797
+ } else {
798
+ // much easier case, usually just in primatives
799
+ for (var attribute in node._attrs) {
800
+ // make sure we only set things that have a value
801
+ if (typeof node._attrs[attribute] !== typeof undefined && attribute != "class" && attribute != "style" && attribute != "id" && typeof node._attrs[attribute] !== undefined && node._attrs[attribute] != null && node._attrs[attribute] != "") {
802
+ props[attribute] = node._attrs[attribute];
803
+ }
804
+ }
805
+ }
806
+ // support sandboxed environments which
807
+ // will hate iframe tags but love webview
808
+ let tag = node.tagName.toLowerCase();
809
+ if (globalThis.HaxStore && globalThis.HaxStore.instance && globalThis.HaxStore.instance._isSandboxed && tag === "iframe") {
810
+ tag = "webview";
811
+ }
812
+ let slotContent = '';
813
+ // if hax store around, allow it to get slot content of the node
814
+ if (globalThis.HaxStore && globalThis.HaxStore.instance) {
815
+ slotContent = await globalThis.HaxStore.instance.getHAXSlot(node);
816
+ } else {
817
+ // if HAX isn't around, just return the innerHTML as a string for asignment to content
818
+ slotContent = node.innerHTML;
819
+ }
820
+ // support fallback on inner text if there were no nodes
821
+ if (slotContent == "") {
822
+ slotContent = node.innerText;
823
+ }
824
+ let element = {
825
+ tag: tag,
826
+ properties: props,
827
+ content: slotContent
828
+ };
829
+ if (eventName !== null) {
830
+ element.eventName = eventName;
831
+ }
832
+ return element;
589
833
  }
@@ -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.11",
3
+ "version": "9.0.13",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -42,20 +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.18",
46
46
  "@haxtheweb/open-apis": "^9.0.11",
47
+ "commander": "12.1.0",
48
+ "node-html-parser": "6.1.13",
47
49
  "ejs": "3.1.10",
48
- "picocolors": "1.0.1",
49
- "commander": "12.1.0"
50
+ "js-yaml": "4.1.0",
51
+ "picocolors": "1.0.1"
50
52
  },
51
53
  "devDependencies": {
52
- "nodemon": "^3.1.7",
53
54
  "@babel/cli": "^7.24.6",
54
55
  "@babel/core": "^7.24.6",
55
56
  "@babel/preset-env": "7.24.6",
56
57
  "@babel/register": "^7.24.6",
57
- "@custom-elements-manifest/analyzer": "^0.10.2",
58
+ "@custom-elements-manifest/analyzer": "^0.10.3",
58
59
  "babel-plugin-transform-dynamic-import": "^2.1.0",
59
- "commit-and-tag-version": "12.4.1"
60
+ "commit-and-tag-version": "12.4.1",
61
+ "nodemon": "^3.1.7"
60
62
  }
61
63
  }