@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/README.md +68 -4
- package/dist/index.js +355 -192
- package/dist/index.js.map +1 -1
- package/package.json +10 -9
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: "
|
|
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 =
|
|
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
|
|
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:
|
|
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
|
|
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}
|
|
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 = [
|
|
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(
|
|
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
|
-
|
|
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
|
|
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
|
|
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 (
|
|
746
|
-
case
|
|
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(`${
|
|
878
|
+
fragmentFields.push(`${propName} { ...${forCms12 ? 'PageIContentListItem' : 'BlockData'} }`);
|
|
751
879
|
else
|
|
752
|
-
fragmentFields.push(`${
|
|
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(`${
|
|
886
|
+
fragmentFields.push(`${propName} { ...LinkItemData }`);
|
|
759
887
|
break;
|
|
760
888
|
default:
|
|
761
|
-
fragmentFields.push(`${
|
|
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(`${
|
|
897
|
+
fragmentFields.push(`${propName} { __typename }`);
|
|
767
898
|
break;
|
|
768
899
|
}
|
|
769
900
|
break;
|
|
770
|
-
case
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
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
|
-
|
|
777
|
-
|
|
918
|
+
}
|
|
919
|
+
case IntegrationApi.PropertyDataType.URL:
|
|
920
|
+
fragmentFields.push(forCms12 ? propName : `${propName} { ...LinkData }`);
|
|
778
921
|
break;
|
|
779
|
-
case
|
|
780
|
-
fragmentFields.push(`${
|
|
922
|
+
case IntegrationApi.PropertyDataType.CONTENT_REFERENCE:
|
|
923
|
+
fragmentFields.push(`${propName} { ...ReferenceData }`);
|
|
781
924
|
break;
|
|
782
|
-
case
|
|
925
|
+
case IntegrationApi.PropertyDataType.COMPONENT: {
|
|
783
926
|
const componentType = typeProps[propKey].contentType;
|
|
784
|
-
|
|
785
|
-
|
|
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(
|
|
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
|
-
|
|
796
|
-
|
|
797
|
-
|
|
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 {
|
|
947
|
-
import {
|
|
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 = (
|
|
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 {
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
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
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
{
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
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
|
|
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 = "
|
|
1503
|
+
var version = "3.0.0";
|
|
1341
1504
|
var name = "opti-cms";
|
|
1342
1505
|
var APP = {
|
|
1343
1506
|
version: version,
|