@remkoj/optimizely-cms-cli 2.0.4 → 3.0.0

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/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { globSync, glob } from 'glob';
2
2
  import dotenv from 'dotenv';
3
3
  import { expand } from 'dotenv-expand';
4
- import { getCmsIntegrationApiConfigFromEnvironment, createClient } from '@remkoj/optimizely-cms-api';
4
+ import { getCmsIntegrationApiConfigFromEnvironment, createClient, OptiCmsVersion, IntegrationApi } from '@remkoj/optimizely-cms-api';
5
5
  import yargs from 'yargs';
6
6
  import path from 'node:path';
7
7
  import fs from 'node:fs';
@@ -41,7 +41,7 @@ function createOptiCmsApp(scriptName, version, epilogue) {
41
41
  .option("cms_url", { alias: "cu", description: "Optimizely CMS URL", string: true, type: "string", demandOption: isDemanded(config.base), default: config.base, coerce: (val) => new URL(val) })
42
42
  .option("client_id", { alias: "ci", description: "API Client ID", string: true, type: "string", demandOption: isDemanded(config.clientId), default: config.clientId })
43
43
  .option('client_secret', { alias: "cs", description: "API Client Secrent", string: true, type: "string", demandOption: isDemanded(config.clientSecret), default: config.clientSecret })
44
- .option('user_id', { alias: "uid", description: "Impersonate user id", string: true, type: "string", demandOption: false, default: config.actAs })
44
+ .option('user_id', { alias: "u", description: "Impersonate user id", string: true, type: "string", demandOption: false, default: config.actAs })
45
45
  .option('verbose', { description: "Enable logging", boolean: true, type: 'boolean', demandOption: false, default: config.debug })
46
46
  .demandCommand(1, 1)
47
47
  .showHelpOnFail(true)
@@ -92,6 +92,18 @@ function parseArgs({ client_id, client_secret, cms_url, user_id, verbose, path:
92
92
  };
93
93
  }
94
94
 
95
+ function createCmsClient(args) {
96
+ const { _config: cfg } = parseArgs(args);
97
+ const baseConfig = getCmsIntegrationApiConfigFromEnvironment();
98
+ const client = createClient({
99
+ ...baseConfig,
100
+ ...cfg
101
+ });
102
+ if (cfg.debug)
103
+ process.stdout.write(chalk.gray(`${figures.arrowRight} Connecting to ${cfg.base.href} as ${cfg.actAs ?? cfg.clientId}\n`));
104
+ return client;
105
+ }
106
+
95
107
  const StylesPushCommand = {
96
108
  command: "styles:push",
97
109
  describe: "Push Visual Builder style definitions into the CMS (create/replace)",
@@ -102,7 +114,11 @@ const StylesPushCommand = {
102
114
  },
103
115
  handler: async (args) => {
104
116
  const { _config: cfg, excludeTemplates, templates, ...opts } = parseArgs(args);
105
- const client = createClient(cfg);
117
+ const client = createCmsClient(args);
118
+ if (client.runtimeCmsVersion == OptiCmsVersion.CMS12) {
119
+ process.stdout.write(chalk.gray(`${figures.cross} Styles are not supported on CMS12\n`));
120
+ return;
121
+ }
106
122
  process.stdout.write(chalk.yellowBright(`${figures.arrowRight} Pushing (create/replace) DisplayStyles into Optimizely CMS\n`));
107
123
  const styleDefinitionFiles = await glob("./**/*.opti-style.json", {
108
124
  cwd: opts.components
@@ -161,23 +177,21 @@ function isNotNullOrUndefined(i) {
161
177
  return i ? true : false;
162
178
  }
163
179
 
164
- function createCmsClient(args) {
165
- const { _config: cfg } = parseArgs(args);
166
- const client = createClient(cfg);
167
- if (cfg.debug)
168
- process.stdout.write(chalk.gray(`${figures.arrowRight} Connecting to ${cfg.base.href} as ${cfg.actAs ?? cfg.clientId}\n`));
169
- return client;
170
- }
171
-
172
180
  const contentTypesBuilder = yargs => {
173
181
  yargs.option('excludeTypes', { alias: 'ect', description: "Exclude these content types", string: true, type: 'array', demandOption: false, default: [] });
174
182
  yargs.option('excludeBaseTypes', { alias: 'ebt', description: "Exclude these base types", string: true, type: 'array', demandOption: false, default: ['folder', 'media', 'image', 'video'] });
175
183
  yargs.option("baseTypes", { alias: 'b', description: "Select only these base types", string: true, type: 'array', demandOption: false, default: [] });
176
184
  yargs.option("types", { alias: 't', description: "Select only these types", string: true, type: 'array', demandOption: false, default: [] });
185
+ yargs.option('all', { alias: 'a', description: "Include non-supported base types", boolean: true, type: 'boolean', demandOption: false, default: false });
177
186
  return yargs;
178
187
  };
188
+ function getEnumOptions(enumObject) {
189
+ return Object.keys(enumObject).filter((item) => {
190
+ return isNaN(Number(item));
191
+ });
192
+ }
179
193
  async function getContentTypes(client, args, pageSize = 100, allowSystem = false) {
180
- const { _config: cfg, excludeBaseTypes, excludeTypes, baseTypes, types } = parseArgs(args);
194
+ const { _config: cfg, excludeBaseTypes, excludeTypes, baseTypes, types, all } = parseArgs(args);
181
195
  process.stdout.write(chalk.yellowBright(`${figures.arrowRight} Pulling Content Types from Optimizely CMS\n`));
182
196
  if (cfg.debug)
183
197
  process.stdout.write(chalk.gray(`${figures.arrowRight} Fetching page 1 of ? (${pageSize} items per page)\n`));
@@ -195,7 +209,24 @@ async function getContentTypes(client, args, pageSize = 100, allowSystem = false
195
209
  process.stdout.write(chalk.gray(`${figures.arrowRight} Fetched ${results.length} Content-Types from Optimizely CMS\n`));
196
210
  process.stdout.write(chalk.gray(`${figures.arrowRight} Filtering Content-Types based upon arguments\n`));
197
211
  }
198
- const contentTypes = results.filter(data => {
212
+ const validBaseTypes = getEnumOptions(IntegrationApi.ContentBaseType).map(x => x.toLowerCase());
213
+ const allContentTypes = all ?
214
+ // If we're returning all content types, including non-supported base types, make sure the base type is always set
215
+ results.map(contentType => {
216
+ return {
217
+ ...contentType,
218
+ baseType: contentType.baseType ?? 'default'
219
+ };
220
+ }) :
221
+ // Otherwise filter out any non supported content base type
222
+ results.filter(contentType => {
223
+ const baseType = (contentType.baseType ?? 'default').toLowerCase();
224
+ const isValid = validBaseTypes.includes(baseType);
225
+ if (!isValid && cfg.debug)
226
+ process.stdout.write(chalk.gray(`${figures.arrowRight} Removing ${contentType.key} as it has an unsupported base type: ${baseType}\n`));
227
+ return isValid;
228
+ });
229
+ const contentTypes = allContentTypes.filter(data => {
199
230
  // Remove items based upon filters
200
231
  const keepType = (!excludeBaseTypes.includes(data.baseType)) &&
201
232
  (!excludeTypes.includes(data.key)) &&
@@ -217,8 +248,8 @@ async function getContentTypes(client, args, pageSize = 100, allowSystem = false
217
248
  if (cfg.debug)
218
249
  process.stdout.write(chalk.gray(`${figures.arrowRight} Applied content type filters, reduced from ${results.length} to ${contentTypes.length} items\n`));
219
250
  return {
220
- all: results,
221
- contentTypes
251
+ all: allContentTypes,
252
+ contentTypes: contentTypes
222
253
  };
223
254
  }
224
255
 
@@ -232,6 +263,8 @@ const stylesBuilder = yargs => {
232
263
  return newArgs;
233
264
  };
234
265
  async function getStyles(client, args, pageSize = 100) {
266
+ if (client.runtimeCmsVersion == OptiCmsVersion.CMS12)
267
+ return { all: [], styles: [] };
235
268
  const { _config: cfg, excludeBaseTypes, excludeTypes, excludeNodeTypes, excludeTemplates, baseTypes, types, nodes, templates, templateTypes } = parseArgs(args);
236
269
  process.stdout.write(chalk.yellowBright(`${figures.arrowRight} Pulling Style-Definitions from Optimizely CMS\n`));
237
270
  if (cfg.debug)
@@ -272,11 +305,21 @@ async function getStyles(client, args, pageSize = 100) {
272
305
  process.stdout.write(chalk.gray(`${figures.arrowRight} Skipping Style-Defintion ${data.key} - Style is defined at component type level and component type filtering is active\n`));
273
306
  return false;
274
307
  }
308
+ if (templateType != 'component' && types.length > 0) {
309
+ if (cfg.debug)
310
+ process.stdout.write(chalk.gray(`${figures.arrowRight} Skipping Style-Defintion ${data.key} - Style is targeting the ${templateType} level and component type selection is active\n`));
311
+ return false;
312
+ }
275
313
  if (data.nodeType && isExcluded(data.nodeType, excludeNodeTypes, nodes)) {
276
314
  if (cfg.debug)
277
315
  process.stdout.write(chalk.gray(`${figures.arrowRight} Skipping Style-Defintion ${data.key} - Style is defined at node type level and node type filtering is active\n`));
278
316
  return false;
279
317
  }
318
+ if (templateType != 'node' && nodes.length > 0) {
319
+ if (cfg.debug)
320
+ process.stdout.write(chalk.gray(`${figures.arrowRight} Skipping Style-Defintion ${data.key} - Style is targeting the ${templateType} level and node type selection is active\n`));
321
+ return false;
322
+ }
280
323
  return true;
281
324
  });
282
325
  if (cfg.debug)
@@ -321,7 +364,12 @@ const StylesListCommand = {
321
364
  command: "styles:list",
322
365
  describe: "List Visual Builder style definitions from the CMS",
323
366
  handler: async (args) => {
324
- const { all: results } = await getStyles(createCmsClient(args), { ...args, excludeBaseTypes: [], excludeNodeTypes: [], excludeTemplates: [], excludeTypes: [], baseTypes: [], nodes: [], templates: [], types: [], templateTypes: [] });
367
+ const client = createCmsClient(args);
368
+ if (client.runtimeCmsVersion == OptiCmsVersion.CMS12) {
369
+ process.stdout.write(chalk.gray(`${figures.cross} Styles are not supported on CMS12\n`));
370
+ return;
371
+ }
372
+ const { all: results } = await getStyles(client, { ...args, excludeBaseTypes: [], excludeNodeTypes: [], excludeTemplates: [], excludeTypes: [], baseTypes: [], nodes: [], templates: [], types: [], templateTypes: [], all: false });
325
373
  const styles = new Table({
326
374
  head: [
327
375
  chalk.yellow(chalk.bold("Name")),
@@ -357,8 +405,13 @@ const StylesPullCommand = {
357
405
  handler: async (args) => {
358
406
  const { _config: cfg, components: basePath, force, definitions } = parseArgs(args);
359
407
  const client = createCmsClient(args);
408
+ if (client.runtimeCmsVersion == OptiCmsVersion.CMS12) {
409
+ process.stdout.write(chalk.gray(`${figures.cross} Styles are not supported on CMS12\n`));
410
+ return;
411
+ }
360
412
  const { styles: filteredResults } = await getStyles(client, args);
361
413
  //#region Create & Write opti-style.json files
414
+ process.stdout.write(chalk.gray(`${figures.arrowRight} Start creating .opti-style.json files\n`));
362
415
  const typeFiles = {};
363
416
  const updatedTemplates = (await Promise.all(filteredResults.map(async (displayTemplate) => {
364
417
  let itemPath = undefined;
@@ -384,18 +437,28 @@ const StylesPullCommand = {
384
437
  fs.mkdirSync(itemPath, { recursive: true });
385
438
  // Write Style JSON
386
439
  const filePath = path.join(itemPath, `${displayTemplate.key}.opti-style.json`);
440
+ const outputTemplate = { ...displayTemplate };
441
+ if (outputTemplate.createdBy)
442
+ delete outputTemplate.createdBy;
443
+ if (outputTemplate.lastModifiedBy)
444
+ delete outputTemplate.lastModifiedBy;
387
445
  if (fs.existsSync(filePath)) {
388
446
  if (!force) {
389
447
  if (cfg.debug)
390
448
  process.stdout.write(chalk.gray(`${figures.cross} Skipping style file for ${displayTemplate.key} - File already exists\n`));
391
- return;
392
449
  }
450
+ else {
451
+ if (cfg.debug) {
452
+ process.stdout.write(chalk.gray(`${figures.arrowRight} Overwriting style file for ${displayTemplate.key}\n`));
453
+ }
454
+ fs.writeFileSync(filePath, JSON.stringify(outputTemplate, undefined, 2));
455
+ }
456
+ }
457
+ else {
393
458
  if (cfg.debug)
394
- process.stdout.write(chalk.gray(`${figures.arrowRight} Overwriting style file for ${displayTemplate.key}\n`));
459
+ process.stdout.write(chalk.gray(`${figures.arrowRight} Creating style file for ${displayTemplate.key} in ${filePath}\n`));
460
+ fs.writeFileSync(filePath, JSON.stringify(outputTemplate, undefined, 2));
395
461
  }
396
- else if (cfg.debug)
397
- process.stdout.write(chalk.gray(`${figures.arrowRight} Creating style file for ${displayTemplate.key}\n`));
398
- fs.writeFileSync(filePath, JSON.stringify(displayTemplate, undefined, 2));
399
462
  if (!typeFiles[targetType]) {
400
463
  typeFiles[targetType] = {
401
464
  filePath: path.join(typesPath, 'displayTemplates.ts'),
@@ -408,6 +471,7 @@ const StylesPullCommand = {
408
471
  //#endregion
409
472
  //#region Create needed definition files
410
473
  if (definitions) {
474
+ process.stdout.write(chalk.gray(`${figures.arrowRight} Start creating displayTemplates.ts files\n`));
411
475
  for (const targetId of Object.getOwnPropertyNames(typeFiles)) {
412
476
  const { filePath: typeFilePath, templates } = typeFiles[targetId];
413
477
  if (fs.existsSync(typeFilePath)) {
@@ -417,12 +481,15 @@ const StylesPullCommand = {
417
481
  continue;
418
482
  }
419
483
  if (cfg.debug)
420
- process.stdout.write(chalk.gray(`${figures.arrowRight} Overwriting definition file for ${targetId}\n`));
484
+ process.stdout.write(chalk.gray(`${figures.arrowRight} Overwriting definition file for ${targetId} - ${typeFilePath}\n`));
421
485
  }
422
486
  else if (cfg.debug)
423
- process.stdout.write(chalk.gray(`${figures.arrowRight} Creating definition file for ${targetId}\n`));
487
+ process.stdout.write(chalk.gray(`${figures.arrowRight} Creating definition file for ${targetId} - ${typeFilePath}\n`));
424
488
  // Write Style definition
425
- const imports = ['import type { LayoutProps } from "@remkoj/optimizely-cms-react/components"', 'import type { ReactNode } from "react"'];
489
+ const imports = [
490
+ 'import type { LayoutProps } from "@remkoj/optimizely-cms-react"',
491
+ 'import type { ReactNode } from "react"'
492
+ ];
426
493
  const typeContents = [];
427
494
  const props = [];
428
495
  let typeId = targetId.split('/', 2)[1];
@@ -430,9 +497,9 @@ const StylesPullCommand = {
430
497
  const importPath = path.relative(path.dirname(typeFilePath), displayTemplateFile).replaceAll('\\', '/');
431
498
  imports.push(`import type ${displayTemplate.key}Styles from "./${importPath}"`);
432
499
  typeContents.push(`export type ${displayTemplate.key}Props = LayoutProps<typeof ${displayTemplate.key}Styles>`);
433
- typeContents.push(`export type ${displayTemplate.key}ComponentProps<DT extends Record<string, any> = Record<string, any>> = {
434
- data: DT
435
- layoutProps: ${displayTemplate.key}Props | undefined
500
+ typeContents.push(`export type ${displayTemplate.key}ComponentProps<DT extends Record<string, any> = Record<string, any>> = {
501
+ data: DT
502
+ layoutProps: ${displayTemplate.key}Props | undefined
436
503
  } & JSX.IntrinsicElements['div']`);
437
504
  typeContents.push(`export type ${displayTemplate.key}Component<DT extends Record<string, any> = Record<string, any>> = (props: ${displayTemplate.key}ComponentProps<DT>) => ReactNode`);
438
505
  typeContents.push('');
@@ -443,13 +510,25 @@ const StylesPullCommand = {
443
510
  if (typeId) {
444
511
  typeId = ucFirst$1(typeId);
445
512
  typeContents.push('');
446
- typeContents.push(`export type ${typeId}LayoutProps = ${props.join(' | ')}
447
- export type ${typeId}ComponentProps<DT extends Record<string, any> = Record<string, any>, LP extends ${typeId}LayoutProps = ${typeId}LayoutProps> = {
448
- data: DT
449
- layoutProps: LP | undefined
450
- } & JSX.IntrinsicElements['div']
451
-
452
- export type ${typeId}Component<DT extends Record<string, any> = Record<string, any>, LP extends ${typeId}LayoutProps = ${typeId}LayoutProps> = (props: ${typeId}ComponentProps<DT,LP>) => ReactNode`);
513
+ typeContents.push(`export type ${typeId}LayoutProps = ${props.join(' | ')}
514
+ export type ${typeId}ComponentProps<DT extends Record<string, any> = Record<string, any>, LP extends ${typeId}LayoutProps = ${typeId}LayoutProps> = {
515
+ data: DT
516
+ layoutProps: LP | undefined
517
+ } & JSX.IntrinsicElements['div']
518
+
519
+ export type ${typeId}Component<DT extends Record<string, any> = Record<string, any>, LP extends ${typeId}LayoutProps = ${typeId}LayoutProps> = (props: ${typeId}ComponentProps<DT,LP>) => ReactNode
520
+
521
+ export function isDefaultProps(props?: ${typeId}LayoutProps | null) : props is ${templates.filter(t => t.data.isDefault).at(0)?.data?.key}Props
522
+ {
523
+ return props?.template == "${templates.filter(t => t.data.isDefault).at(0)?.data?.key}"
524
+ }`);
525
+ templates.forEach(t => {
526
+ typeContents.push(`
527
+ export function is${t.data.key}Props(props?: ${typeId}LayoutProps | null) : props is ${t.data.key}Props
528
+ {
529
+ return props?.template == "${t.data.key}"
530
+ }`);
531
+ });
453
532
  }
454
533
  fs.writeFileSync(typeFilePath, imports.join("\n") + "\n\n" + typeContents.join("\n"));
455
534
  }
@@ -474,12 +553,16 @@ const StylesCreateCommand = {
474
553
  handler: async (args) => {
475
554
  const { components: basePath } = parseArgs(args);
476
555
  const client = createCmsClient(args);
556
+ if (client.runtimeCmsVersion == OptiCmsVersion.CMS12) {
557
+ process.stdout.write(chalk.gray(`${figures.cross} Styles are not supported on CMS12\n`));
558
+ return;
559
+ }
477
560
  const allowedBaseTypes = ['section', 'element'];
478
561
  // Prepare
479
562
  process.stdout.write(chalk.yellowBright(chalk.bold(`Reading current information from Optimizely CMS\n`)));
480
563
  const [{ contentTypes }, { styles }] = await Promise.all([
481
- getContentTypes(client, { ...args, excludeBaseTypes: [], excludeTypes: [], baseTypes: allowedBaseTypes, types: [] }),
482
- getStyles(client, { ...args, excludeBaseTypes: [], excludeNodeTypes: [], excludeTemplates: [], excludeTypes: [], baseTypes: allowedBaseTypes, types: [], nodes: [], components: '', templates: [], templateTypes: [] })
564
+ getContentTypes(client, { ...args, excludeBaseTypes: [], excludeTypes: [], baseTypes: allowedBaseTypes, types: [], all: false }),
565
+ getStyles(client, { ...args, excludeBaseTypes: [], excludeNodeTypes: [], excludeTemplates: [], excludeTypes: [], baseTypes: allowedBaseTypes, types: [], nodes: [], components: '', templates: [], templateTypes: [], all: false })
483
566
  ]);
484
567
  const styleKeys = styles.map(x => x.key);
485
568
  // Define the configuration
@@ -562,9 +645,18 @@ const TypesPullCommand = {
562
645
  process.stdout.write(chalk.yellow(`${figures.cross} Skipping type definition for ${contentType.displayName} (${contentType.key}) - File already exists\n`));
563
646
  return contentType.key;
564
647
  }
648
+ const outContentType = { ...contentType };
649
+ if (outContentType.source || outContentType.source == "")
650
+ delete outContentType.source;
651
+ if (outContentType.features)
652
+ delete outContentType.features;
653
+ if (outContentType.usage)
654
+ delete outContentType.usage;
655
+ if (outContentType.lastModifiedBy)
656
+ delete outContentType.lastModifiedBy;
565
657
  if (debug)
566
658
  process.stdout.write(chalk.gray(`${figures.arrowRight} Writing type definition for ${contentType.displayName} (${contentType.key})\n`));
567
- fs.writeFileSync(typeFile, JSON.stringify(contentType, undefined, 2));
659
+ fs.writeFileSync(typeFile, JSON.stringify(outContentType, undefined, 2));
568
660
  return contentType.key;
569
661
  }).filter(x => x);
570
662
  process.stdout.write(chalk.green(chalk.bold(`${figures.tick} Created/updated type definitions for ${updatedTypes.join(', ')}\n`)));
@@ -603,7 +695,20 @@ const TypesPushCommand = {
603
695
  // Output selected types
604
696
  const results = await Promise.all(typeDefinitions.map(type => {
605
697
  process.stdout.write(chalk.yellowBright(` ${figures.arrowRight} Pushing ${type.definition.displayName} from ${type.file}\n`));
606
- return client.contentTypes.contentTypesPut(type.definition.key, type.definition, force)
698
+ const outType = { ...type.definition };
699
+ if (outType.source || outType.source == "")
700
+ delete outType.source;
701
+ if (outType.created || outType.created == "")
702
+ delete outType.created;
703
+ if (outType.lastModified || outType.lastModified == "")
704
+ delete outType.lastModified;
705
+ if (outType.lastModifiedBy || outType.lastModifiedBy == "")
706
+ delete outType.lastModifiedBy;
707
+ if (outType.features)
708
+ delete outType.features;
709
+ if (outType.usage)
710
+ delete outType.usage;
711
+ return client.contentTypes.contentTypesPut(outType.key, outType, force)
607
712
  .then(ct => { return { key: type.definition.key, type: ct, file: type.file }; })
608
713
  .catch(e => { return { key: type.definition.key, type: type.definition, file: type.file, error: e }; });
609
714
  }));
@@ -660,21 +765,27 @@ function getTypeFolder(list, type) {
660
765
  return list.filter(x => x.type == type).at(0)?.path;
661
766
  }
662
767
 
768
+ /**
769
+ * Keep track of all generated properties
770
+ */
771
+ let generatedProps = [];
663
772
  const NextJsQueriesCommand = {
664
773
  command: "nextjs:fragments",
665
774
  describe: "Create the GrapQL Fragments for a Next.JS / Optimizely Graph structure",
666
775
  builder,
667
776
  handler: async (args, opts) => {
777
+ generatedProps = [];
668
778
  // Prepare
669
779
  const { loadedContentTypes, createdTypeFolders } = opts || {};
670
780
  const { components: basePath, _config: { debug }, force } = parseArgs(args);
671
- const { contentTypes, all: allContentTypes } = loadedContentTypes ?? await getContentTypes(createCmsClient(args), args);
781
+ const client = createCmsClient(args);
782
+ const { contentTypes, all: allContentTypes } = loadedContentTypes ?? await getContentTypes(client, args);
672
783
  // Start process
673
784
  process.stdout.write(chalk.yellowBright(`${figures.arrowRight} Generating GraphQL fragments for ${contentTypes.map(x => x.key).join(', ')}\n`));
674
785
  const typeFolders = createdTypeFolders ?? createTypeFolders(contentTypes, basePath, debug);
675
786
  const updatedTypes = contentTypes.map(contentType => {
676
787
  const typePath = getTypeFolder(typeFolders, contentType.key);
677
- return createGraphFragments(contentType, typePath, basePath, force, debug, allContentTypes);
788
+ return createGraphFragments(contentType, typePath, basePath, force, debug, allContentTypes, client.runtimeCmsVersion == OptiCmsVersion.CMS12);
678
789
  }).filter(x => x).flat();
679
790
  // Report outcome
680
791
  if (updatedTypes.length > 0)
@@ -683,9 +794,10 @@ const NextJsQueriesCommand = {
683
794
  process.stdout.write(chalk.yellowBright(`${figures.arrowRight} No GraphQL fragments created/updated\n`));
684
795
  if (!opts)
685
796
  process.stdout.write(chalk.green(chalk.bold(figures.tick + " Done")) + "\n");
797
+ generatedProps = [];
686
798
  }
687
799
  };
688
- function createGraphFragments(contentType, typePath, basePath, force, debug, contentTypes) {
800
+ function createGraphFragments(contentType, typePath, basePath, force, debug, contentTypes, forCms12 = false) {
689
801
  const returnValue = [];
690
802
  const baseType = contentType.baseType ?? 'default';
691
803
  const baseQueryFile = path.join(typePath, `${contentType.key}.${baseType}.graphql`);
@@ -703,7 +815,7 @@ function createGraphFragments(contentType, typePath, basePath, force, debug, con
703
815
  else if (debug) {
704
816
  process.stdout.write(chalk.gray(`${figures.arrowRight} Creating ${contentType.displayName} (${contentType.key}) base fragment\n`));
705
817
  }
706
- const { fragment, propertyTypes } = createInitialFragment(contentType);
818
+ const { fragment, propertyTypes } = createInitialFragment(contentType, false, undefined, forCms12);
707
819
  fs.writeFileSync(baseQueryFile, fragment);
708
820
  returnValue.push(contentType.key);
709
821
  let dependencies = Array.isArray(propertyTypes) ? [...propertyTypes] : [];
@@ -715,14 +827,15 @@ function createGraphFragments(contentType, typePath, basePath, force, debug, con
715
827
  console.warn(`🟠 The content type ${dep[0]} has been referenced, but is not found in the Optimizely CMS instance`);
716
828
  return;
717
829
  }
718
- const propertyFragmentFile = path.join(basePath, propContentType.baseType ?? 'default', propContentType.key, `${propContentType.key}.property.graphql`);
830
+ const fullTypeName = forCms12 ? contentType.key + propContentType.key : propContentType.key;
831
+ const propertyFragmentFile = path.join(basePath, propContentType.baseType, propContentType.key, `${fullTypeName}.property.graphql`);
719
832
  const propertyFragmentDir = path.dirname(propertyFragmentFile);
720
833
  if (!fs.existsSync(propertyFragmentDir))
721
834
  fs.mkdirSync(propertyFragmentDir, { recursive: true });
722
835
  if (!fs.existsSync(propertyFragmentFile) || force) {
723
836
  if (debug)
724
837
  process.stdout.write(chalk.gray(`${figures.arrowRight} Writing ${propContentType.displayName} (${propContentType.key}) property fragment\n`));
725
- const propContentTypeInfo = createInitialFragment(propContentType, true);
838
+ const propContentTypeInfo = createInitialFragment(propContentType, true, contentType, forCms12);
726
839
  fs.writeFileSync(propertyFragmentFile, propContentTypeInfo.fragment);
727
840
  returnValue.push(propContentType.key);
728
841
  if (Array.isArray(propContentTypeInfo.propertyTypes))
@@ -733,7 +846,7 @@ function createGraphFragments(contentType, typePath, basePath, force, debug, con
733
846
  }
734
847
  return returnValue.length > 0 ? returnValue : undefined;
735
848
  }
736
- function createInitialFragment(contentType, forProperty = false) {
849
+ function createInitialFragment(contentType, forProperty = false, forBaseType, forCms12 = false) {
737
850
  const propertyTypes = [];
738
851
  const fragmentFields = [];
739
852
  const typeProps = contentType.properties ?? {};
@@ -741,60 +854,109 @@ function createInitialFragment(contentType, forProperty = false) {
741
854
  // Exclude system properties, which are not present in Optimizely Graph
742
855
  if (['experience', 'section'].includes(contentType.baseType) && ['AdditionalData', 'UnstructuredData', 'Layout'].includes(propKey))
743
856
  return;
857
+ // Exclude CMS 12 System Properties
858
+ if (forCms12 && ['Categories'].includes(propKey))
859
+ return;
860
+ const propType = typeProps[propKey].type;
861
+ const isConflict = generatedProps.some(x => x.propName == propKey && x.propType != propType);
862
+ const propName = isConflict ? `${contentType.key}${propKey}: ${propKey}` : propKey;
744
863
  // Write the property
745
- switch (typeProps[propKey].type) {
746
- case "array":
864
+ switch (propType) {
865
+ case IntegrationApi.PropertyDataType.ARRAY:
747
866
  switch (typeProps[propKey].items.type) {
867
+ case "integer":
868
+ if (typeProps[propKey].format == 'categorization')
869
+ fragmentFields.push(`${propName} { Id, Name, Description }`);
870
+ else
871
+ fragmentFields.push(propName);
872
+ break;
873
+ case "string":
874
+ fragmentFields.push(propName);
875
+ break;
748
876
  case "content":
749
877
  if (contentType.baseType == 'page' || contentType.baseType == 'experience')
750
- fragmentFields.push(`${propKey} { ...BlockData }`);
878
+ fragmentFields.push(`${propName} { ...${forCms12 ? 'PageIContentListItem' : 'BlockData'} }`);
751
879
  else
752
- fragmentFields.push(`${propKey} { ...IContentListItem }`);
880
+ fragmentFields.push(`${propName} { ...IContentListItem }`);
753
881
  break;
754
882
  case "component":
755
883
  const componentType = typeProps[propKey].items.contentType;
756
884
  switch (componentType) {
757
885
  case 'link':
758
- fragmentFields.push(`${propKey} { ...LinkItemData }`);
886
+ fragmentFields.push(`${propName} { ...LinkItemData }`);
759
887
  break;
760
888
  default:
761
- fragmentFields.push(`${propKey} { ...${componentType}Data }`);
889
+ fragmentFields.push(`${propName} { ...${componentType}Data }`);
762
890
  break;
763
891
  }
764
892
  break;
893
+ case "contentReference":
894
+ fragmentFields.push(`${propName} { ...ReferenceData }`);
895
+ break;
765
896
  default:
766
- fragmentFields.push(`${propKey} { __typename }`);
897
+ fragmentFields.push(`${propName} { __typename }`);
767
898
  break;
768
899
  }
769
900
  break;
770
- case "string":
771
- if (typeProps[propKey].format == "html")
772
- fragmentFields.push(`${propKey} { json, html }`);
773
- else
774
- fragmentFields.push(propKey);
901
+ case IntegrationApi.PropertyDataType.STRING: {
902
+ const propDetails = typeProps[propKey];
903
+ switch (propDetails.format ?? "") {
904
+ case 'html':
905
+ fragmentFields.push(forCms12 ? `${propName} { Structure, Html }` : `${propName} { json, html }`);
906
+ break;
907
+ case 'shortString':
908
+ case 'selectOne':
909
+ case '':
910
+ fragmentFields.push(propName);
911
+ break;
912
+ default:
913
+ if (!isConflict)
914
+ console.warn(chalk.redBright(`❗ Unsupported string format "${propDetails.format}" for ${contentType.key}.${propKey}; add it manually to the fragment if you need to access this field`));
915
+ break;
916
+ }
775
917
  break;
776
- case "url":
777
- fragmentFields.push(`${propKey} { ...LinkData }`);
918
+ }
919
+ case IntegrationApi.PropertyDataType.URL:
920
+ fragmentFields.push(forCms12 ? propName : `${propName} { ...LinkData }`);
778
921
  break;
779
- case "contentReference":
780
- fragmentFields.push(`${propKey} { ...ReferenceData }`);
922
+ case IntegrationApi.PropertyDataType.CONTENT_REFERENCE:
923
+ fragmentFields.push(`${propName} { ...ReferenceData }`);
781
924
  break;
782
- case "component":
925
+ case IntegrationApi.PropertyDataType.COMPONENT: {
783
926
  const componentType = typeProps[propKey].contentType;
784
- fragmentFields.push(`${propKey} { ...${componentType}PropertyData }`);
785
- propertyTypes.push([componentType, true]);
927
+ if (forCms12 && componentType == "link") {
928
+ fragmentFields.push(`${propName} { ...LinkItemData }`);
929
+ }
930
+ else {
931
+ const componentFragmentName = forCms12 ? contentType.key + componentType : componentType + 'Property';
932
+ fragmentFields.push(`${propName} { ...${componentFragmentName}Data }`);
933
+ propertyTypes.push([componentType, true]);
934
+ }
935
+ break;
936
+ }
937
+ case IntegrationApi.PropertyDataType.BINARY:
938
+ fragmentFields.push(`${propName} { ...BinaryData }`);
786
939
  break;
787
940
  default:
788
- fragmentFields.push(propKey);
941
+ fragmentFields.push(propName);
789
942
  break;
790
943
  }
944
+ generatedProps.push({
945
+ propType: typeProps[propKey].type,
946
+ propName: propKey
947
+ });
791
948
  });
792
949
  if (contentType.baseType == "experience")
793
950
  fragmentFields.push('...ExperienceData');
794
- if (fragmentFields.length == 0)
795
- fragmentFields.push('_metadata { key }');
796
- const tpl = `fragment ${contentType.key}${forProperty ? 'Property' : ''}Data on ${contentType.key}${forProperty ? 'Property' : ''} {
797
- ${fragmentFields.join("\n ")}
951
+ if (fragmentFields.length == 0) {
952
+ if (forCms12)
953
+ fragmentFields.push('empty: _metadata: ContentLink { key: GuidValue }');
954
+ else
955
+ fragmentFields.push('empty: _metadata { key }');
956
+ }
957
+ const fragmentTarget = forProperty ? (forCms12 ? (forBaseType?.key ?? '') + contentType.key : contentType.key + 'Property') : contentType.key;
958
+ const tpl = `fragment ${fragmentTarget}Data on ${fragmentTarget} {
959
+ ${fragmentFields.join("\n ")}
798
960
  }`;
799
961
  return {
800
962
  fragment: tpl,
@@ -889,84 +1051,84 @@ function getDisplayTemplateInfo$1(contentType, typePath) {
889
1051
  }
890
1052
  const Templates = {
891
1053
  // Default Template for all components without specifics
892
- default: (contentType, varName, displayTemplate, baseDisplayTemplate) => `import { CmsComponent } from "@remkoj/optimizely-cms-react";
893
- import { ${contentType.key}DataFragmentDoc, type ${contentType.key}DataFragment } from "@/gql/graphql";${displayTemplate ? `
894
- import { ${displayTemplate} } from "./displayTemplates";` : ''}${baseDisplayTemplate ? `
895
- import { ${baseDisplayTemplate} } from "../styles/displayTemplates";` : ''}
896
-
897
- /**
898
- * ${contentType.displayName}
899
- * ${contentType.description}
900
- */
901
- export const ${varName} : CmsComponent<${contentType.key}DataFragment${(displayTemplate || baseDisplayTemplate) ? ', ' + [displayTemplate, baseDisplayTemplate].filter(x => x).join(" | ") : ''}> = ({ data${(displayTemplate || baseDisplayTemplate) ? ', layoutProps' : ''}, children }) => {
902
- const componentName = '${contentType.displayName}'
903
- const componentInfo = '${contentType.description ?? ''}'
904
- return <div className="w-full border-y border-y-solid border-y-slate-900 py-2 mb-4">
905
- <div className="font-bold italic">{ componentName }</div>
906
- <div>{ componentInfo }</div>
907
- { Object.getOwnPropertyNames(data).length > 0 && <pre className="w-full overflow-x-hidden font-mono text-sm bg-slate-200 p-2 rounded-sm border border-solid border-slate-900 text-slate-900">{ JSON.stringify(data, undefined, 4) }</pre> }
908
- { children && <div className="mt-4 mx-4 flex flex-col">{ children }</div>}
909
- </div>
910
- }
911
- ${varName}.displayName = "${contentType.displayName} (${ucFirst(contentType.baseType)}/${contentType.key})"
912
- ${varName}.getDataFragment = () => ['${contentType.key}Data', ${contentType.key}DataFragmentDoc]
913
-
1054
+ default: (contentType, varName, displayTemplate, baseDisplayTemplate) => `import { type CmsComponent } from "@remkoj/optimizely-cms-react";
1055
+ import { ${contentType.key}DataFragmentDoc, type ${contentType.key}DataFragment } from "@/gql/graphql";${displayTemplate ? `
1056
+ import { ${displayTemplate} } from "./displayTemplates";` : ''}${baseDisplayTemplate ? `
1057
+ import { ${baseDisplayTemplate} } from "../styles/displayTemplates";` : ''}
1058
+
1059
+ /**
1060
+ * ${contentType.displayName}
1061
+ * ${contentType.description}
1062
+ */
1063
+ export const ${varName} : CmsComponent<${contentType.key}DataFragment${(displayTemplate || baseDisplayTemplate) ? ', ' + [displayTemplate, baseDisplayTemplate].filter(x => x).join(" | ") : ''}> = ({ data${(displayTemplate || baseDisplayTemplate) ? ', layoutProps' : ''}, children }) => {
1064
+ const componentName = '${contentType.displayName}'
1065
+ const componentInfo = '${contentType.description?.replaceAll("'", "\\'") ?? ''}'
1066
+ return <div className="w-full border-y border-y-solid border-y-slate-900 py-2 mb-4">
1067
+ <div className="font-bold italic">{ componentName }</div>
1068
+ <div>{ componentInfo }</div>
1069
+ { Object.getOwnPropertyNames(data).length > 0 && <pre className="w-full overflow-x-hidden font-mono text-sm bg-slate-200 p-2 rounded-sm border border-solid border-slate-900 text-slate-900">{ JSON.stringify(data, undefined, 4) }</pre> }
1070
+ { children && <div className="mt-4 mx-4 flex flex-col">{ children }</div>}
1071
+ </div>
1072
+ }
1073
+ ${varName}.displayName = "${contentType.displayName} (${ucFirst(contentType.baseType)}/${contentType.key})"
1074
+ ${varName}.getDataFragment = () => ['${contentType.key}Data', ${contentType.key}DataFragmentDoc]
1075
+
914
1076
  export default ${varName}`,
915
1077
  // Template for all page component types
916
- page: (contentType, varName, displayTemplate) => `import { OptimizelyNextPage as CmsComponent } from "@remkoj/optimizely-cms-nextjs";
917
- import { ${contentType.key}DataFragmentDoc, type ${contentType.key}DataFragment } from "@/gql/graphql";${displayTemplate ? `
918
- import { ${displayTemplate} } from "./displayTemplates";` : ''}
919
- import { getSdk } from "@/gql"
920
-
921
- /**
922
- * ${contentType.displayName}
923
- * ${contentType.description}
924
- */
925
- export const ${varName} : CmsComponent<${contentType.key}DataFragment${displayTemplate ? ', ' + displayTemplate : ''}> = ({ data${displayTemplate ? ', layoutProps' : ''}, children }) => {
926
- const componentName = '${contentType.displayName}'
927
- const componentInfo = '${contentType.description ?? ''}'
928
- return <div className="mx-auto px-2 container">
929
- <div className="font-bold italic">{ componentName }</div>
930
- <div>{ componentInfo }</div>
931
- { Object.getOwnPropertyNames(data).length > 0 && <pre className="w-full overflow-x-hidden font-mono text-sm bg-slate-200 p-2 rounded-sm border border-solid border-slate-900 text-slate-900">{ JSON.stringify(data, undefined, 4) }</pre> }
932
- { children && <div className="flex flex-col mt-4 mx-4">{ children }</div>}
933
- </div>
934
- }
935
- ${varName}.displayName = "${contentType.displayName} (${ucFirst(contentType.baseType)}/${contentType.key})"
936
- ${varName}.getDataFragment = () => ['${contentType.key}Data', ${contentType.key}DataFragmentDoc]
937
- ${varName}.getMetaData = async (contentLink, locale, client) => {
938
- const sdk = getSdk(client);
939
- // Add your metadata logic here
940
- return {}
941
- }
942
-
1078
+ page: (contentType, varName, displayTemplate) => `import { type OptimizelyNextPage as CmsComponent } from "@remkoj/optimizely-cms-nextjs";
1079
+ import { ${contentType.key}DataFragmentDoc, type ${contentType.key}DataFragment } from "@/gql/graphql";${displayTemplate ? `
1080
+ import { ${displayTemplate} } from "./displayTemplates";` : ''}
1081
+ import { getSdk } from "@/gql"
1082
+
1083
+ /**
1084
+ * ${contentType.displayName}
1085
+ * ${contentType.description}
1086
+ */
1087
+ export const ${varName} : CmsComponent<${contentType.key}DataFragment${displayTemplate ? ', ' + displayTemplate : ''}> = ({ data${displayTemplate ? ', layoutProps' : ''}, children }) => {
1088
+ const componentName = '${contentType.displayName}'
1089
+ const componentInfo = '${contentType.description?.replaceAll("'", "\\'") ?? ''}'
1090
+ return <div className="mx-auto px-2 container">
1091
+ <div className="font-bold italic">{ componentName }</div>
1092
+ <div>{ componentInfo }</div>
1093
+ { Object.getOwnPropertyNames(data).length > 0 && <pre className="w-full overflow-x-hidden font-mono text-sm bg-slate-200 p-2 rounded-sm border border-solid border-slate-900 text-slate-900">{ JSON.stringify(data, undefined, 4) }</pre> }
1094
+ { children && <div className="flex flex-col mt-4 mx-4">{ children }</div>}
1095
+ </div>
1096
+ }
1097
+ ${varName}.displayName = "${contentType.displayName} (${ucFirst(contentType.baseType)}/${contentType.key})"
1098
+ ${varName}.getDataFragment = () => ['${contentType.key}Data', ${contentType.key}DataFragmentDoc]
1099
+ ${varName}.getMetaData = async (contentLink, locale, client) => {
1100
+ const sdk = getSdk(client);
1101
+ // Add your metadata logic here
1102
+ return {}
1103
+ }
1104
+
943
1105
  export default ${varName}`,
944
1106
  // Template for all experience component types
945
- experience: (contentType, varName, displayTemplate) => `import { OptimizelyNextPage as CmsComponent } from "@remkoj/optimizely-cms-nextjs";
946
- import { ${contentType.key}DataFragmentDoc, type ${contentType.key}DataFragment } from "@/gql/graphql";
947
- import { type Maybe, type ICompositionNode, type ExperienceDataFragment } from "@/gql/graphql";
948
- import { OptimizelyComposition, isNode, CmsEditable } from "@remkoj/optimizely-cms-react/rsc";${displayTemplate ? `
949
- import { ${displayTemplate} } from "./displayTemplates";` : ''}
950
- import { getSdk } from "@/gql"
951
-
952
- /**
953
- * ${contentType.displayName}
954
- * ${contentType.description}
955
- */
956
- export const ${varName} : CmsComponent<${contentType.key}DataFragment${displayTemplate ? ', ' + displayTemplate : ''}> = ({ data${displayTemplate ? ', layoutProps' : ''} }) => {
957
- const composition = (data as ExperienceDataFragment).composition as Maybe<ICompositionNode>
958
- return <CmsEditable as="div" className="mx-auto px-2 container" cmsFieldName="unstructuredData">
959
- { composition && isNode(composition) && <OptimizelyComposition node={composition} /> }
960
- </CmsEditable>
961
- }
962
- ${varName}.displayName = "${contentType.displayName} (${ucFirst(contentType.baseType)}/${contentType.key})"
963
- ${varName}.getDataFragment = () => ['${contentType.key}Data', ${contentType.key}DataFragmentDoc]
964
- ${varName}.getMetaData = async (contentLink, locale, client) => {
965
- const sdk = getSdk(client);
966
- // Add your metadata logic here
967
- return {}
968
- }
969
-
1107
+ experience: (contentType, varName, displayTemplate) => `import { type OptimizelyNextPage as CmsComponent } from "@remkoj/optimizely-cms-nextjs";
1108
+ import { getFragmentData } from "@/gql/fragment-masking";
1109
+ import { ExperienceDataFragmentDoc, CompositionDataFragmentDoc, ${contentType.key}DataFragmentDoc, type ${contentType.key}DataFragment } from "@/gql/graphql";
1110
+ import { OptimizelyComposition, isNode, CmsEditable } from "@remkoj/optimizely-cms-react/rsc";${displayTemplate ? `
1111
+ import { ${displayTemplate} } from "./displayTemplates";` : ''}
1112
+ import { getSdk } from "@/gql"
1113
+
1114
+ /**
1115
+ * ${contentType.displayName}
1116
+ * ${contentType.description}
1117
+ */
1118
+ export const ${varName} : CmsComponent<${contentType.key}DataFragment${displayTemplate ? ', ' + displayTemplate : ''}> = ({ data${displayTemplate ? ', layoutProps' : ''} }) => {
1119
+ const composition = getFragmentData(CompositionDataFragmentDoc, getFragmentData(ExperienceDataFragmentDoc, data)?.composition)
1120
+ return <CmsEditable as="div" className="mx-auto px-2 container" cmsFieldName="unstructuredData">
1121
+ { composition && isNode(composition) && <OptimizelyComposition node={composition} /> }
1122
+ </CmsEditable>
1123
+ }
1124
+ ${varName}.displayName = "${contentType.displayName} (${ucFirst(contentType.baseType)}/${contentType.key})"
1125
+ ${varName}.getDataFragment = () => ['${contentType.key}Data', ${contentType.key}DataFragmentDoc]
1126
+ ${varName}.getMetaData = async (contentLink, locale, client) => {
1127
+ const sdk = getSdk(client);
1128
+ // Add your metadata logic here
1129
+ return {}
1130
+ }
1131
+
970
1132
  export default ${varName}`
971
1133
  };
972
1134
 
@@ -1010,15 +1172,14 @@ function createSpecificNode(template, templatePath, force = false, debug = false
1010
1172
  if (!fs.existsSync(templatePath))
1011
1173
  fs.mkdirSync(templatePath, { recursive: true });
1012
1174
  const displayTemplateName = getDisplayTemplateInfo(template, templatePath);
1013
- const component = `import { type CmsLayoutComponent } from "@remkoj/optimizely-cms-react";
1014
- import { extractSettings } from "@remkoj/optimizely-cms-react/components";${displayTemplateName ? `
1015
- import { ${displayTemplateName} } from "../displayTemplates";` : ''}
1016
-
1017
- export const ${template.key} : CmsLayoutComponent<${displayTemplateName ?? '{}'}> = ({ contentLink, layoutProps, children }) => {
1018
- const layout = extractSettings(layoutProps);
1019
- return (<div className="vb:${template.nodeType} vb:${template.nodeType}:${template.key}">{ children }</div>);
1020
- }
1021
-
1175
+ const component = `import { extractSettings, type CmsLayoutComponent } from "@remkoj/optimizely-cms-react/rsc";${displayTemplateName ? `
1176
+ import { ${displayTemplateName} } from "../displayTemplates";` : ''}
1177
+
1178
+ export const ${template.key} : CmsLayoutComponent<${displayTemplateName ?? '{}'}> = ({ contentLink, layoutProps, children }) => {
1179
+ const layout = extractSettings(layoutProps);
1180
+ return (<div className="vb:${template.nodeType} vb:${template.nodeType}:${template.key}">{ children }</div>);
1181
+ }
1182
+
1022
1183
  export default ${template.key};`;
1023
1184
  fs.writeFileSync(nodeFile, component);
1024
1185
  }
@@ -1035,17 +1196,16 @@ function createGenericNode(basePath, force, debug) {
1035
1196
  }
1036
1197
  else if (debug)
1037
1198
  process.stdout.write(chalk.gray(`${figures.arrowRight} Creating generic node component\n`));
1038
- const nodeContent = `import { type CmsLayoutComponent } from "@remkoj/optimizely-cms-react"
1039
- import { CmsEditable } from '@remkoj/optimizely-cms-react/rsc'
1040
-
1041
- export const VisualBuilderNode : CmsLayoutComponent = ({ contentLink, layoutProps, children }) =>
1042
- {
1043
- let className = \`vb:\${layoutProps?.layoutType}\`
1044
- if (layoutProps && layoutProps.layoutType == "section")
1045
- return <CmsEditable as="div" className={ className } cmsId={ contentLink.key }>{ children }</CmsEditable>
1046
- return <div className={ className }>{ children }</div>
1047
- }
1048
-
1199
+ const nodeContent = `import { CmsEditable, type CmsLayoutComponent } from '@remkoj/optimizely-cms-react/rsc'
1200
+
1201
+ export const VisualBuilderNode : CmsLayoutComponent = ({ contentLink, layoutProps, children }) =>
1202
+ {
1203
+ let className = \`vb:\${layoutProps?.layoutType}\`
1204
+ if (layoutProps && layoutProps.layoutType == "section")
1205
+ return <CmsEditable as="div" className={ className } cmsId={ contentLink.key }>{ children }</CmsEditable>
1206
+ return <div className={ className }>{ children }</div>
1207
+ }
1208
+
1049
1209
  export default VisualBuilderNode`;
1050
1210
  fs.writeFileSync(nodeItem, nodeContent);
1051
1211
  }
@@ -1080,6 +1240,10 @@ const NextJsFactoryCommand = {
1080
1240
  entries.forEach((e, i) => {
1081
1241
  if (isImportInfoList(e) && typeof e.prefix == 'string')
1082
1242
  switch (e.prefix) {
1243
+ case "Video":
1244
+ case "Image":
1245
+ entries[i].prefix = ["Media", e.prefix, "Component"];
1246
+ break;
1083
1247
  case "Experience":
1084
1248
  entries[i].prefix = [e.prefix, "Page"];
1085
1249
  break;
@@ -1233,34 +1397,34 @@ function processTypeListFolder(baseType, baseTypePath, force = false, debug = fa
1233
1397
  }
1234
1398
  function generateFactory(components, typeName) {
1235
1399
  const needsPrefixFunction = components.some(isPrefixedImportInfoList);
1236
- const factoryContent = `// Auto generated dictionary
1237
- import { ComponentTypeDictionary } from "@remkoj/optimizely-cms-react";
1238
- ${components.map(x => `import ${x.component} from "${x.path}";`).join("\n")}
1239
-
1240
- ${needsPrefixFunction ? `// Prefix entries - if needed
1400
+ const factoryContent = `// Auto generated dictionary
1401
+ import { type ComponentTypeDictionary } from "@remkoj/optimizely-cms-react";
1402
+ ${components.map(x => `import ${x.component} from "${x.path}";`).join("\n")}
1403
+
1404
+ ${needsPrefixFunction ? `// Prefix entries - if needed
1241
1405
  ${components.filter(isPrefixedImportInfoList).map(x => Array.isArray(x.prefix) ?
1242
1406
  x.prefix.map(z => `prefixDictionaryEntries(${x.component}, "${z}");`).join("\n") :
1243
- `prefixDictionaryEntries(${x.component}, "${x.prefix}");`).join("\n")}
1244
-
1245
- ` : ''}// Build dictionary
1246
- export const ${typeName}Dictionary : ComponentTypeDictionary = [
1247
- ${components.map(x => x.isList == true ? `...${x.component}` : `{
1248
- type: "${x.type}",
1249
- component: ${x.component}
1250
- }`).join(",\n ")}
1251
- ];
1252
-
1253
- // Export dictionary
1254
- export default ${typeName}Dictionary;${needsPrefixFunction ? `
1255
-
1256
- // Helper functions
1257
- function prefixDictionaryEntries(list: ComponentTypeDictionary, prefix: string) : ComponentTypeDictionary
1258
- {
1259
- list.forEach((component, idx, dictionary) => {
1260
- dictionary[idx].type = typeof component.type == 'string' ? prefix + "/" + component.type : [ prefix, ...component.type ]
1261
- });
1262
- return list;
1263
- }` : ''}
1407
+ `prefixDictionaryEntries(${x.component}, "${x.prefix}");`).join("\n")}
1408
+
1409
+ ` : ''}// Build dictionary
1410
+ export const ${typeName}Dictionary : ComponentTypeDictionary = [
1411
+ ${components.map(x => x.isList == true ? `...${x.component}` : `{
1412
+ type: "${x.type}",
1413
+ component: ${x.component}
1414
+ }`).join(",\n ")}
1415
+ ];
1416
+
1417
+ // Export dictionary
1418
+ export default ${typeName}Dictionary;${needsPrefixFunction ? `
1419
+
1420
+ // Helper functions
1421
+ function prefixDictionaryEntries(list: ComponentTypeDictionary, prefix: string) : ComponentTypeDictionary
1422
+ {
1423
+ list.forEach((component, idx, dictionary) => {
1424
+ dictionary[idx].type = typeof component.type == 'string' ? prefix + "/" + component.type : [ prefix, ...component.type ]
1425
+ });
1426
+ return list;
1427
+ }` : ''}
1264
1428
  `;
1265
1429
  return factoryContent;
1266
1430
  }
@@ -1299,8 +1463,7 @@ const CmsVersionCommand = {
1299
1463
  command: "cms:version",
1300
1464
  describe: "Get the CMS Version information",
1301
1465
  handler: async (args) => {
1302
- const { _config: cfg } = parseArgs(args);
1303
- const client = createClient(cfg);
1466
+ const client = createCmsClient(args);
1304
1467
  process.stdout.write(chalk.yellowBright(`${figures.arrowRight} Reading version information Optimizely CMS\n`));
1305
1468
  const versionInfo = await client.getInstanceInfo();
1306
1469
  const info = new Table({
@@ -1337,7 +1500,7 @@ const commands = [
1337
1500
  CmsVersionCommand
1338
1501
  ];
1339
1502
 
1340
- var version = "2.0.4";
1503
+ var version = "3.0.0";
1341
1504
  var name = "opti-cms";
1342
1505
  var APP = {
1343
1506
  version: version,