contensis-cli 1.6.1-beta.21 → 1.6.1-beta.23

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.
@@ -352,3 +352,115 @@ Or we can refer to other fields in the `entry` variable
352
352
  ```handlebars
353
353
  {{entry.text}}{% if entry.heading %} - {{entry.heading}}{% endif %}
354
354
  ```
355
+
356
+ ### Transforming fields inside Canvas blocks
357
+
358
+ Canvas fields contain an array of blocks (objects with `id`, `type`, `value`, `properties`). Some blocks are components (`_component` type with `properties.component`) that hold nested fields inside `value`. To transform those inner fields - for example converting a rich text field inside a component to canvas format - you need to rebuild the canvas array by iterating over the blocks and selectively transforming the ones you need.
359
+
360
+ This is done using the `capture` tag to build the output as a JSON string, then `from_json | json` to validate and minify the result.
361
+
362
+ #### Example: Convert rich text to canvas inside a component block
363
+
364
+ Given a canvas field containing a `linkedComponent` block with a `richText` field:
365
+
366
+ ```json
367
+ {
368
+ "id": "5b03fe6a",
369
+ "type": "_component",
370
+ "properties": { "component": "linkedComponent" },
371
+ "value": {
372
+ "entry": [{ "sys": { "id": "abc123", "dataFormat": "entry" } }],
373
+ "title": "My component",
374
+ "richText": "<h2>Original rich text</h2>",
375
+ "canvas": null
376
+ }
377
+ }
378
+ ```
379
+
380
+ This template converts `richText` to canvas via `html_to_canvas`, storing it in the `canvas` field. Other blocks pass through unchanged:
381
+
382
+ ```handlebars
383
+ {% capture out %}
384
+ [
385
+ {%- for b in value -%}
386
+ {%- if forloop.index0 > 0 %},{% endif -%}
387
+ {%- if b.properties.component == "linkedComponent" -%}
388
+ {
389
+ "id": {{ b.id | json }},
390
+ "type": {{ b.type | json }},
391
+ "properties": {{ b.properties | json }},
392
+ "value": {
393
+ "entry": {{ b.value.entry | json }},
394
+ "title": {{ b.value.title | json }},
395
+ "richText": {{ b.value.richText | json }},
396
+ "canvas": {% if b.value.richText and b.value.richText != "" %}{{ b.value.richText | html_to_canvas | json }}{% else %}{{ b.value.canvas | json }}{% endif %}
397
+ }
398
+ }
399
+ {%- else -%}
400
+ {{ b | json }}
401
+ {%- endif -%}
402
+ {%- endfor -%}
403
+ ]
404
+ {% endcapture %}
405
+ {{ out | from_json | json }}
406
+ ```
407
+
408
+ #### How it works
409
+
410
+ 1. `{% capture out %}` starts building the output as a JSON string
411
+ 2. The outer `[` opens the canvas array
412
+ 3. `{% for b in value %}` iterates over every block in the canvas
413
+ 4. `{% if b.properties.component == "linkedComponent" %}` matches the block we want to transform
414
+ 5. Each field in the block is mapped with `|json` to ensure valid JSON output (strings get quotes, objects serialize correctly)
415
+ 6. The `canvas` field conditionally applies `html_to_canvas` when `richText` exists, otherwise passes the existing value through
416
+ 7. `{% else %}` passes non-matching blocks through unchanged via `{{ b | json }}`
417
+ 8. `{% endcapture %}` closes the string, then `{{ out | from_json | json }}` parses and re-serializes to validate the JSON
418
+
419
+ #### Why `|json` is needed on every mapped field
420
+
421
+ Without it, strings would render bare (invalid JSON: `hello` instead of `"hello"`) and objects would render as `[object Object]`. The `|json` filter handles strings, objects, arrays, nulls, and booleans correctly.
422
+
423
+ #### Handling nested component blocks
424
+
425
+ If a canvas block contains a repeatable array of sub-components, you can nest the same pattern. For example, a `nestingLinkedComponent` block has `linkedComponent[]` items, each with their own `richText` and `canvas`:
426
+
427
+ ```handlebars
428
+ {% capture out %}
429
+ [
430
+ {%- for b in value -%}
431
+ {%- if forloop.index0 > 0 %},{% endif -%}
432
+ {%- if b.properties.component == "nestingLinkedComponent" -%}
433
+ {
434
+ "id": {{ b.id | json }},
435
+ "type": {{ b.type | json }},
436
+ "properties": {{ b.properties | json }},
437
+ "value": {
438
+ "linkedComponent": [
439
+ {%- for it in b.value.linkedComponent -%}
440
+ {%- if forloop.index0 > 0 %},{% endif -%}
441
+ {
442
+ "entry": {{ it.entry | json }},
443
+ "title": {{ it.title | json }},
444
+ "richText": {{ it.richText | json }},
445
+ "canvas": {% if it.richText and it.richText != "" %}{{ it.richText | html_to_canvas | json }}{% else %}{{ it.canvas | json }}{% endif %}
446
+ }
447
+ {%- endfor -%}
448
+ ],
449
+ "singleLinkedComponent": {
450
+ "entry": {{ b.value.singleLinkedComponent.entry | json }},
451
+ "title": {{ b.value.singleLinkedComponent.title | json }},
452
+ "richText": {{ b.value.singleLinkedComponent.richText | json }},
453
+ "canvas": {% if b.value.singleLinkedComponent.richText and b.value.singleLinkedComponent.richText != "" %}{{ b.value.singleLinkedComponent.richText | html_to_canvas | json }}{% else %}{{ b.value.singleLinkedComponent.canvas | json }}{% endif %}
454
+ }
455
+ }
456
+ }
457
+ {%- else -%}
458
+ {{ b | json }}
459
+ {%- endif -%}
460
+ {%- endfor -%}
461
+ ]
462
+ {% endcapture %}
463
+ {{ out | from_json | json }}
464
+ ```
465
+
466
+ The same `richText` to `canvas` conversion is applied to each item in the repeatable `linkedComponent` array and to the singular `singleLinkedComponent` block.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "contensis-cli",
3
- "version": "1.6.1-beta.21",
3
+ "version": "1.6.1-beta.23",
4
4
  "description": "A fully featured Contensis command line interface with a shell UI provides simple and intuitive ways to manage or profile your content in any NodeJS terminal.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -25,7 +25,7 @@
25
25
  "package-lock": "npm i --package-lock-only --workspaces false"
26
26
  },
27
27
  "author": "Zengenti",
28
- "license": "ISC",
28
+ "license": "GPL-3.0",
29
29
  "dependencies": {
30
30
  "@action-validator/core": "^0.6.0",
31
31
  "@inquirer/prompts": "^8.2.0",
@@ -43,7 +43,7 @@
43
43
  "inquirer-command-prompt": "^0.1.0",
44
44
  "keytar": "^7.9.0",
45
45
  "lodash": "^4.17.21",
46
- "migratortron": "^1.0.0-beta.88",
46
+ "migratortron": "^1.0.0-beta.89",
47
47
  "nanospinner": "^1.2.0",
48
48
  "node-fetch": "^2.6.7",
49
49
  "parse-git-config": "^3.0.0",
@@ -35,18 +35,19 @@ Example call:
35
35
  .command('renderer')
36
36
  .description('assign a renderer to a node')
37
37
  .argument('<"node path or id">', 'the path or id of the node to update')
38
- .argument('<rendererUuid>', 'the renderer uuid to assign to the node')
38
+ .argument('<rendererUuidOrName>', 'the renderer uuid, id or name to assign to the node')
39
39
  .option(
40
40
  '--is-partial-match-root',
41
41
  'should the renderer be used as the partial match root for the node',
42
42
  false
43
43
  )
44
- .usage('<"node path or id"> <rendererUuid>')
44
+ .usage('<"node path or id"> <rendererUuidOrName>')
45
45
  .addHelpText(
46
46
  'after',
47
47
  `
48
48
  Example call:
49
- > set node renderer /path 1502f64e-e9b1-436b-b62f-e273f639ecb6 --is-partial-match-root\n`
49
+ > set node renderer /path 1502f64e-e9b1-436b-b62f-e273f639ecb6 --is-partial-match-root
50
+ > set node renderer /path my-block --is-partial-match-root\n`
50
51
  )
51
52
  .action(async (nodePathOrId: string, rendererId: string, opts) => {
52
53
  await cliCommand(['set', 'node', 'renderer'], opts).CreateOrUpdateNode(
@@ -325,6 +325,10 @@ export const LogMessages = {
325
325
  `[${env}] Unable to find renderer ${Logger.highlightText(
326
326
  name
327
327
  )} in project ${projectId}`,
328
+ resolved: (env: string, name: string, uuid: string) =>
329
+ `[${env}] Resolved renderer ${Logger.highlightText(
330
+ name
331
+ )} to uuid ${Logger.highlightText(uuid)}`,
328
332
  created: (env: string, name: string, projectId: string) =>
329
333
  `[${env}] Created renderer ${Logger.highlightText(
330
334
  name
@@ -5,6 +5,7 @@ import inquirer from 'inquirer';
5
5
  import fetch from 'node-fetch';
6
6
  import path from 'path';
7
7
  import clone from 'rfdc';
8
+ import type { CommanderError } from 'commander';
8
9
 
9
10
  import { Component, ContentType, Project } from 'contensis-core-api';
10
11
  import {
@@ -74,9 +75,9 @@ import { GetTagGroupsArgs } from 'migratortron/dist/services/TagGroupsMigrationS
74
75
  let insecurePasswordWarningShown = false;
75
76
 
76
77
  class ContensisCli {
77
- static quit = (error?: Error) => {
78
+ static quit = (error?: CommanderError) => {
78
79
  process.removeAllListeners('exit');
79
- const exitCode = error ? 1 : 0;
80
+ const exitCode = error?.exitCode ?? (error ? 1 : 0);
80
81
 
81
82
  // console.info(`\nExiting contensis-cli with exit code: ${exitCode}\n`);
82
83
  process.exit(exitCode);
@@ -2818,7 +2819,32 @@ class ContensisCli {
2818
2819
  if (nodeById) existingNode = nodeById;
2819
2820
  }
2820
2821
 
2821
- // TODO: resolve any renderer id to its uuid so we can support passing the regular id for ease of use
2822
+ // Resolve a friendly renderer id/name to its uuid for ease of use
2823
+ const rendererRef = nodeUpdates.renderer?.id;
2824
+ if (rendererRef && !isUuid(rendererRef)) {
2825
+ const [, renderers] = await contensis.renderers.GetRenderers();
2826
+ const match = renderers?.find(
2827
+ r =>
2828
+ r.id?.toLowerCase() === rendererRef.toLowerCase() ||
2829
+ r.name?.toLowerCase() === rendererRef.toLowerCase()
2830
+ );
2831
+ if (match) {
2832
+ log.info(
2833
+ messages.renderers.resolved(
2834
+ currentEnv,
2835
+ rendererRef,
2836
+ match.uuid
2837
+ )
2838
+ );
2839
+ nodeUpdates.renderer.id = match.uuid;
2840
+ } else {
2841
+ log.error(
2842
+ messages.renderers.failedGet(currentEnv, rendererRef, currentProject)
2843
+ );
2844
+ return;
2845
+ }
2846
+ }
2847
+
2822
2848
  if (!existingNode) {
2823
2849
  // Build a node object to create or reject if there is not enough data
2824
2850
 
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const LIB_VERSION = "1.6.1-beta.21";
1
+ export const LIB_VERSION = "1.6.1-beta.23";