@pega/custom-dx-components 25.1.13 → 25.1.15

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pega/custom-dx-components",
3
- "version": "25.1.13",
3
+ "version": "25.1.15",
4
4
  "description": "Utility for building custom UI components",
5
5
  "bin": "./bin/index.js",
6
6
  "keywords": [
package/src/cli.js CHANGED
@@ -19,6 +19,7 @@ import {
19
19
  deleteVersion,
20
20
  importArchive,
21
21
  exportArchive,
22
+ exportRC,
22
23
  importComponent,
23
24
  initProject,
24
25
  listComponents,
@@ -84,6 +85,7 @@ const TASKS = {
84
85
  DELETE_VERSION: 'deleteVersion',
85
86
  IMPORT_ARCHIVE: 'importArchive',
86
87
  EXPORT_ARCHIVE: 'exportArchive',
88
+ EXPORT_RC: 'exportRC',
87
89
  IMPORT_COMP: 'importComponent',
88
90
  AUTHENTICATE: 'authenticate',
89
91
  UPGRADE: 'upgrade',
@@ -234,6 +236,10 @@ export default async (args = []) => {
234
236
  exportArchive(options);
235
237
  break;
236
238
  }
239
+ case TASKS.EXPORT_RC: {
240
+ exportRC(options);
241
+ break;
242
+ }
237
243
  case TASKS.IMPORT_COMP: {
238
244
  importComponent(options);
239
245
  break;
@@ -287,4 +293,4 @@ export default async (args = []) => {
287
293
  break;
288
294
  }
289
295
  }
290
- };
296
+ }
@@ -13,6 +13,7 @@ import {
13
13
  getComponentDirectoryPath,
14
14
  getComponents,
15
15
  getPegaServerConfig,
16
+ getServerType,
16
17
  sanitize,
17
18
  validateSemver,
18
19
  showVersion,
@@ -37,7 +38,6 @@ const copyComponentTemplate = async options => {
37
38
  });
38
39
  };
39
40
 
40
-
41
41
  const compileMustacheTemplates = async (
42
42
  componentDirectory,
43
43
  {
@@ -306,6 +306,7 @@ addDebugLog("create", "", "+");
306
306
  );
307
307
  } else {
308
308
  const componentDefaults = getConfigDefaults();
309
+ const serverType = getServerType();
309
310
  const maxChars = isLibraryBased ? getMaxComponentNameCount() : getStandaloneMaxComponentNameCount();
310
311
  const questions = [
311
312
  {
@@ -354,7 +355,14 @@ addDebugLog("create", "", "+");
354
355
  },
355
356
  {
356
357
  name: 'componentName',
357
- message: `Enter component name (required) (${maxChars} max chars)`,
358
+ message: value => {
359
+ if (serverType === "launchpad") {
360
+ return `Enter component name (required)`;
361
+ }
362
+ else {
363
+ return `Enter component name (required) (${maxChars} max chars)`;
364
+ }
365
+ },
358
366
  validate: value => {
359
367
  /* value should not be empty
360
368
  It should not have spaces
@@ -362,7 +370,7 @@ addDebugLog("create", "", "+");
362
370
  Only case-insensitive alphanumeric values are allowed
363
371
  */
364
372
  if (value && !/^\d/.test(value) && value === sanitize(value)) {
365
- if (value.length > maxChars) {
373
+ if (value.length > maxChars && serverType != "launchpad") {
366
374
  return `${value.length - maxChars} too many characters.`
367
375
  }
368
376
  return true;
@@ -14,6 +14,7 @@ import { checkPathAccess, showVersion ,
14
14
  compileMustacheTemplate,
15
15
  getComponentDirectoryPath,
16
16
  getPegaServerConfig,
17
+ getServerType,
17
18
  getComponents,
18
19
  sanitize,
19
20
  validateSemver,
@@ -405,17 +406,25 @@ addDebugLog("createAll", "", "+");
405
406
  );
406
407
  } else {
407
408
  const maxPrefixCount = isLibraryBased ? getMaxPrefixCount() : getStandaloneMaxPrefixCount();
409
+ const serverType = getServerType();
408
410
  const questions = [
409
411
  {
410
412
  name: 'prefix',
411
- message: `Enter prefix label (${maxPrefixCount} max chars)`,
413
+ message: value => {
414
+ if (serverType === "launchpad") {
415
+ return `Enter prefix label`;
416
+ }
417
+ else {
418
+ return `Enter prefix label (${maxPrefixCount} max chars)`;
419
+ }
420
+ },
412
421
  validate: value => {
413
422
  /* value should not be empty
414
423
  It should not start with a number
415
424
  Only case-insensitive alphanumeric values are allowed
416
425
  */
417
426
  if (value && !/^\d/.test(value) ) {
418
- if (value.length > maxPrefixCount) {
427
+ if (value.length > maxPrefixCount && serverType != "launchpad") {
419
428
  return `${value.length - maxPrefixCount} too many characters.`
420
429
  }
421
430
  return true;
@@ -0,0 +1,356 @@
1
+ import chalk from 'chalk';
2
+ import path from 'path';
3
+ import { join } from 'path';
4
+ import fetch from 'node-fetch';
5
+ import archiver from 'archiver';
6
+ import fs from 'fs';
7
+ import {
8
+ addDebugLog,
9
+ getConfigDefaults,
10
+ getLibraryArchiveDirectories,
11
+ getLibraryArchivesVersions,
12
+ setConfigExportRelativePath,
13
+ getComponentDirectoryPath,
14
+ getPegaServerConfig,
15
+ forceDefaultsUpdate } from '../../util.js';
16
+
17
+
18
+
19
+ export const SOURCE_OF_COMPONENT_TYPES = {
20
+ SERVER: 'Server',
21
+ LOCAL: 'Local'
22
+ };
23
+
24
+
25
+ export const getFilePathQuestions = async () => {
26
+ addDebugLog("getFilePathQuestions", "", "");
27
+
28
+ console.log("\nFile path examples:");
29
+ if (path.sep === "/") {
30
+ console.log("\tMac OS/Unix:\t/Users/name/...");
31
+ }
32
+ else {
33
+ console.log("\tWindows:\tC:\\Users\\name\\...\n");
34
+ }
35
+
36
+ const configDef = getConfigDefaults();
37
+
38
+ const currentDirectory = process.cwd();
39
+ const arPathParts = currentDirectory.split(path.sep);
40
+ let defaultPath = join(arPathParts[0], arPathParts[1], arPathParts[2], configDef.exportRelativePath);
41
+ let startDirectory = join(arPathParts[0], arPathParts[1], arPathParts[2]);
42
+
43
+ if (path.sep === "/") {
44
+ defaultPath = "/".concat(defaultPath);
45
+ startDirectory = "/".concat(startDirectory).concat("/");
46
+ }
47
+
48
+ return [
49
+ {
50
+ name: 'filePath',
51
+ type: 'fuzzypath',
52
+ message: 'Enter full file path of directory to export zip file:',
53
+ default: defaultPath,
54
+ excludePath: nodePath => nodePath.startsWith('node_modules'),
55
+ // excludePath :: (String) -> Bool
56
+ // excludePath to exclude some paths from the file-system scan
57
+ excludeFilter: nodePath => nodePath.indexOf('/.') >= 0,
58
+ // excludeFilter :: (String) -> Bool
59
+ // excludeFilter to exclude some paths from the final list, e.g. '.'
60
+ itemType: 'directory',
61
+ // itemType :: 'any' | 'directory' | 'file'
62
+ // specify the type of nodes to display
63
+ // default value: 'any'
64
+ // example: itemType: 'file' - hides directories from the item list
65
+ rootPath: startDirectory,
66
+ // rootPath :: String
67
+ // Root search directory
68
+ suggestOnly: true,
69
+ // suggestOnly :: Bool
70
+ // Restrict prompt answer to available choices or use them as suggestions
71
+ depthLimit: 2,
72
+ // depthLimit :: integer >= 0
73
+ // Limit the depth of sub-folders to scan
74
+ // Defaults to infinite depth if undefined
75
+ }
76
+ ];
77
+ };
78
+
79
+ export const getFileNameQuestions = async (zipFileList) => {
80
+ addDebugLog("getFileNameQuestions", "", "");
81
+
82
+ return [
83
+ {
84
+ name: 'fileName',
85
+ type: 'rawlist',
86
+ message: `Select zip file`,
87
+ choices: zipFileList
88
+ },
89
+ ];
90
+ };
91
+
92
+ export const getLibraryQuestion = async () => {
93
+ addDebugLog("getLibraryQuestion", "", "");
94
+ const componentDefaults = getConfigDefaults();
95
+ const orgLib = getConfigDefaults();
96
+ const currentOrgLib = `${orgLib.organization}_${orgLib.library}`;
97
+
98
+ const archVersionList = await getLibraryArchiveDirectories("");
99
+
100
+
101
+ return [
102
+ {
103
+ name: 'orgLibName',
104
+ type: 'rawlist',
105
+ message: `Select a library (current library is ${chalk.bold.green(`${currentOrgLib}`)}) `,
106
+ choices: archVersionList
107
+ }
108
+ ];
109
+ };
110
+
111
+ export const getLibraryVersionQuestion = async (newOrgLib) => {
112
+ addDebugLog("getLibraryVersionQuestion", "", "");
113
+
114
+ const archVersionList = await getLibraryArchivesVersions(newOrgLib, "0.0.0");
115
+
116
+ return [
117
+ {
118
+ name: 'selectedVersion',
119
+ type: 'rawlist',
120
+ message: `Select a version `,
121
+ choices: archVersionList
122
+ }
123
+ ];
124
+ };
125
+
126
+ export const getServerOrDirectoryQuestion = async () => {
127
+ addDebugLog("getLibraryVersionQuestion", "", "");
128
+
129
+ return [
130
+ {
131
+ name: 'exportTo',
132
+ type: 'rawlist',
133
+ message: `Export to`,
134
+ choices: [{name: 'Local directory', value: 'Directory'}, {name: 'Server', value: 'Server'}],
135
+ default: 'Directory'
136
+ }
137
+ ];
138
+ };
139
+
140
+ export const getFullArchiveQuestions = async () => {
141
+
142
+ return [
143
+ {
144
+ name: 'fullArchive',
145
+ type: 'rawlist',
146
+ message: 'Type of export',
147
+ choices: [{name: 'Full Archive (code)', value: 'Full'}, {name: 'CL Binary (runtime only)', value: 'Binary'}],
148
+ default: 'Full'
149
+ }
150
+
151
+ ];
152
+ }
153
+
154
+ export const getComponentInfoQuestions = async (rPackageJSON, library) => {
155
+
156
+ return [
157
+ {
158
+ name: 'author',
159
+ message: 'Author of this component',
160
+ default: rPackageJSON.author
161
+ },
162
+ {
163
+ name: 'isLocalized',
164
+ type: 'confirm',
165
+ message: 'Is this component fully localized',
166
+ default: false
167
+ },
168
+ {
169
+ name: 'isAccessible',
170
+ type: 'confirm',
171
+ message: 'Is this component accessible',
172
+ default: false
173
+ },
174
+ {
175
+ name: 'isSecure',
176
+ type: 'confirm',
177
+ message: 'Is this component and dependent modules secure',
178
+ default: false
179
+ },
180
+ {
181
+ name: 'license',
182
+ message: 'License of this component (MIT, Apache, GPL, etc.)',
183
+ default: rPackageJSON.license
184
+ },
185
+ {
186
+ name: 'organization',
187
+ message: 'Organization name',
188
+ default: rPackageJSON.organization
189
+ },
190
+ {
191
+ name: 'library',
192
+ message: 'Library name',
193
+ default: library
194
+ }
195
+ ];
196
+ }
197
+
198
+ export const zipComponent = async componentKey => {
199
+ addDebugLog("zipComponent", `componentKey: ${componentKey}`, "");
200
+ const srcDirectory = await getComponentDirectoryPath(componentKey);
201
+ const buildDirectory = await path.join(path.resolve(), 'dist/components', componentKey);
202
+ const configJson = `${srcDirectory}/config.json`;
203
+ const localizationJson = `${srcDirectory}/localizations.json`;
204
+
205
+ const archive = archiver('zip', { zlib: { level: 9 } });
206
+ const zipChunks = [];
207
+ archive.on('data', chunk => zipChunks.push(chunk));
208
+
209
+ // Add src directory
210
+ archive.directory(srcDirectory, 'src');
211
+
212
+ // Add config.json file
213
+ if (fs.existsSync(configJson)) {
214
+ archive.file(configJson, { name: path.basename(configJson) });
215
+ }
216
+
217
+ let lJson = "";
218
+
219
+ // Add localizations.json file if it exists
220
+ if (fs.existsSync(localizationJson)) {
221
+ archive.file(localizationJson, { name: path.basename(localizationJson) });
222
+ lJson = JSON.parse(Buffer.from(fs.readFileSync((localizationJson))).toString());
223
+ }
224
+
225
+ // Add build directory
226
+ archive.directory(buildDirectory, false);
227
+
228
+ await archive.finalize();
229
+
230
+ const zipBuffer = Buffer.concat(zipChunks);
231
+ console.log(chalk.green(`component zipped with size: ${Math.ceil(zipBuffer.length / 1024)} KB`));
232
+ const zipContent = zipBuffer.toString('base64');
233
+
234
+ //let configContent = Buffer.from(fs.readFileSync(configJson)).toString();
235
+ const configContent = JSON.parse(Buffer.from(fs.readFileSync(configJson)).toString());
236
+ // configContent = configContent.replaceAll("\n", "");
237
+
238
+ let output = { configContent };
239
+ if (lJson != "") {
240
+ output = { ...output, localizationJson: lJson};
241
+ }
242
+
243
+ if (fs.existsSync('dist')) {
244
+ fs.rm('dist', { recursive: true }, err => {
245
+ if (err) {
246
+ throw err;
247
+ }
248
+ });
249
+ }
250
+
251
+
252
+ return { zipContent, ...output};
253
+ };
254
+
255
+ export const zipRC = async (content, archiveDirectory, archiveFileName) => {
256
+ addDebugLog("zipRC", ``, "");
257
+
258
+
259
+
260
+ // const configContent = Buffer.from(fs.readFileSync(configJson)).toString();
261
+ // const mainDirectory = process.cwd();
262
+ // const archiveDirectory = join (mainDirectory, ARCHIVES_PATH, `${componentKey}`, `${version}`);
263
+
264
+
265
+ if (!fs.existsSync(archiveDirectory)) {
266
+ fs.mkdirSync(archiveDirectory, { recursive: true });
267
+ }
268
+
269
+ const archiveFile = join(archiveDirectory, archiveFileName);
270
+
271
+ const output = fs.createWriteStream(archiveFile);
272
+ const archive = archiver('zip', {
273
+ zlib: { level: 9 } // Sets the compression level.
274
+ });
275
+
276
+ const zipChunks = [];
277
+ archive.on('data', chunk => zipChunks.push(chunk));
278
+
279
+
280
+ archive.append( JSON.stringify(content), { name: 'componentData.json' });
281
+
282
+ // pipe archive data to the file
283
+ archive.pipe(output);
284
+
285
+ output.on('end', function() {
286
+ console.log('Data has been drained');
287
+ });
288
+
289
+
290
+ // good practice to catch warnings (ie stat failures and other non-blocking errors)
291
+ archive.on('warning', function(err) {
292
+ if (err.code === 'ENOENT') {
293
+ // log warning
294
+ } else {
295
+ // throw error
296
+ throw err;
297
+ }
298
+ });
299
+
300
+ // good practice to catch this error explicitly
301
+ archive.on('error', function(err) {
302
+ throw err;
303
+ });
304
+
305
+ await archive.finalize();
306
+
307
+ return output;
308
+
309
+ };
310
+
311
+
312
+
313
+ export const updateSavedExportFilePath = async(filePath) => {
314
+ const currentDirectory = process.cwd();
315
+ const arPathParts = currentDirectory.split(path.sep);
316
+ const configDef = getConfigDefaults();
317
+
318
+ let defaultPath = join(path.sep, arPathParts[0], arPathParts[1], arPathParts[2]);
319
+
320
+ if (path.sep != "/") {
321
+ // windows
322
+ if (defaultPath.indexOf(path.sep) === 0) {
323
+ // if start with \\, then remove
324
+ defaultPath = defaultPath.substring(1);
325
+ }
326
+
327
+ if (filePath.indexOf(defaultPath) === 0) {
328
+ let relativePath = "";
329
+ if (filePath !== defaultPath) {
330
+ // same starting path, so we can save the relative
331
+ relativePath = filePath.replace(defaultPath.concat(path.sep), "");
332
+ }
333
+
334
+ await setConfigExportRelativePath(relativePath);
335
+ await forceDefaultsUpdate();
336
+
337
+ }
338
+ }
339
+ else {
340
+ // mac
341
+ if (filePath.indexOf(defaultPath) === 0) {
342
+
343
+ let relativePath = "";
344
+ if (filePath !== defaultPath) {
345
+ // same starting path, so we can save the relative
346
+ relativePath = filePath.replace(defaultPath.concat("/"), "");
347
+ }
348
+
349
+
350
+ await setConfigExportRelativePath(relativePath);
351
+ await forceDefaultsUpdate();
352
+
353
+ }
354
+ }
355
+
356
+ }
@@ -0,0 +1,268 @@
1
+ import fs from 'fs';
2
+ import { join } from 'path';
3
+
4
+ import inquirer from 'inquirer';
5
+ import chalk from 'chalk';
6
+ import { Listr } from 'listr2';
7
+
8
+ import validate from '../validator/index.js';
9
+ import bundleComponent from '../bundle/index.js';
10
+ import { lintComponent } from '../linter/index.js';
11
+
12
+ import {
13
+ checkPathAccess,
14
+ showVersion,
15
+ addDebugLog,
16
+ getComponents,
17
+ getConfigDefaults,
18
+ getPegaServerConfig,
19
+ getLibraryBased,
20
+ getComponentDirectoryPath,
21
+ getServerType
22
+ } from '../../util.js';
23
+ import { TASKS_CONFIG_JSON_FILENAME, PACKAGE_JSON_FILENAME } from '../../constants.js';
24
+ import { getFilePathQuestions, updateSavedExportFilePath, zipComponent, zipRC, getComponentInfoQuestions } from './helper.js';
25
+
26
+
27
+ import inquirerFuzzyPath from '@pega/inquirer-fuzzy-path';
28
+
29
+ const currentDirectory = process.cwd();
30
+ const pegaConfigJsonPath = join(currentDirectory, TASKS_CONFIG_JSON_FILENAME);
31
+
32
+ export default async options => {
33
+ await showVersion();
34
+
35
+ await checkPathAccess(pegaConfigJsonPath);
36
+
37
+ const isLibraryBasedCL = getLibraryBased();
38
+ if (isLibraryBasedCL) {
39
+ console.log(`Command only supported for ${chalk.bold.green('non library mode')} components.`)
40
+ process.exit();
41
+ }
42
+
43
+ const serverType = getServerType();
44
+ if (serverType != "launchpad") {
45
+ console.log(`Command only supported for ${chalk.green('Launchpad')}.`);
46
+ process.exit(1);
47
+ }
48
+
49
+
50
+ addDebugLog("export RC", "", "+");
51
+
52
+ let exportTo = "";
53
+
54
+ if (options.params.length >= 8) {
55
+
56
+ // params pick full, what library/name and path
57
+ // vs no params then we will select the current library
58
+ // let fullArchive = options.params[3];
59
+ // const libraryName = options.params[4];
60
+ // const libraryVersion = options.params[5];
61
+ // const filePath = options.params[6];
62
+ // exportTo = "Directory";
63
+
64
+
65
+ // const currentOrArchive = options.params[7];
66
+
67
+ // await exportArch(filePath, libraryName, libraryVersion, fullArchive, currentOrArchive );
68
+
69
+ }
70
+ else {
71
+ let orgLibName = "";
72
+ let selectedVersion = "";
73
+ let fullArchive = "";
74
+
75
+
76
+ // if (options.params.length >= 5) {
77
+ // fullArchive = options.params[3];
78
+ // orgLibName = options.params[4];
79
+ // if (options.params.length >= 6) {
80
+ // selectedVersion = options.params[5];
81
+ // }
82
+ // }
83
+
84
+ // disable question to build "CL"
85
+ // if (fullArchive === "" ) {
86
+ // const fullArchiveQuestions = await getFullArchiveQuestions();
87
+ // const fullArchiveAnswers = await inquirer.prompt(fullArchiveQuestions);
88
+ // ({ fullArchive } = fullArchiveAnswers);
89
+ // }
90
+
91
+ // force full archive, binary deprecated
92
+ fullArchive = "Full";
93
+
94
+ let filePathQuestions;
95
+ let filePathAnswers;
96
+ let filePath;
97
+ let content;
98
+
99
+ const localComponents = await getComponents();
100
+ const defaultPegaConfig = await getPegaServerConfig();
101
+ const configDef = getConfigDefaults();
102
+
103
+
104
+ if (localComponents.length > 0) {
105
+
106
+ const compAnswers = await inquirer.prompt([
107
+ {
108
+ name: 'componentKey',
109
+ type: 'rawlist',
110
+ message: 'Select component to export as a runtime component',
111
+ choices: localComponents
112
+ },
113
+ {
114
+ name: 'devBuild',
115
+ type: 'confirm',
116
+ message: 'Generate development build ?',
117
+ default: defaultPegaConfig.devBuild
118
+ }
119
+ ]);
120
+
121
+ content = {
122
+ ...compAnswers
123
+ };
124
+
125
+
126
+ inquirer.registerPrompt('fuzzypath', inquirerFuzzyPath);
127
+ const filePathQuestions = await getFilePathQuestions();
128
+ const filePathAnswers = await inquirer.prompt(filePathQuestions);
129
+ const exportPath = filePathAnswers.filePath;
130
+ const componentKey = compAnswers.componentKey;
131
+ const devBuild = compAnswers.devBuild;
132
+
133
+
134
+
135
+ if (!fs.existsSync(exportPath)) {
136
+ console.log(`\Path: ${chalk.yellow(`${exportPath}`)} doesn't exist.`);
137
+ const newPathAnswer = await inquirer.prompt([
138
+ {
139
+ name: 'createNewPath',
140
+ type: 'confirm',
141
+ message: 'Create path',
142
+ default: true
143
+ },
144
+ ]);
145
+ const createNewPath = newPathAnswer.createNewPath;
146
+ if (createNewPath) {
147
+ fs.mkdirSync(exportPath, { recursive: true });
148
+
149
+ await updateSavedExportFilePath(exportPath);
150
+ }
151
+ else {
152
+ console.log(chalk.green("Store not exported."));
153
+ process.exit();
154
+ }
155
+ }
156
+
157
+ const archiveFileName = `${componentKey}_${configDef.version}.RC.zip`;
158
+
159
+ if (fs.existsSync(join(exportPath, archiveFileName))) {
160
+ console.log(`\nFile: ${chalk.yellow(`${archiveFileName}`)} already exists in ${chalk.yellow(`${exportPath}`)}.`);
161
+ const overwriteQuestion = await inquirer.prompt([
162
+ {
163
+ name: 'overWriteFile',
164
+ type: 'confirm',
165
+ message: 'Overwrite',
166
+ default: true
167
+ },
168
+ ]);
169
+
170
+ if (!overwriteQuestion.overWriteFile) {
171
+ console.log(chalk.green("Archive not created."));
172
+ process.exit();
173
+ }
174
+ }
175
+
176
+ console.log(chalk.yellow(`\n>> You are about to create a zip file containing an independent component `));
177
+ console.log(chalk.yellow(`>> that others can import directly into Launchpad.`));
178
+ console.log(chalk.yellow(`>> You will be asked a series of questions for the benefit of others`));
179
+ console.log(chalk.yellow(`>> who might choose to use your component.`));
180
+ console.log(chalk.yellow(`\n>> These answers will be recorded in a json file within the zip file.`));
181
+ console.log(chalk.yellow(`>> These questions will NOT be used as a gate to prevent the creation of this component.\n`));
182
+
183
+
184
+ const currentDirectory = process.cwd();
185
+ const packagePath = join (currentDirectory, PACKAGE_JSON_FILENAME);
186
+ let retrievedPackageData;
187
+
188
+ try {
189
+ retrievedPackageData = fs.readFileSync(packagePath);
190
+ }
191
+ catch (ex) {
192
+ console.log(chalk.red(`\nUnable to retrieve package.json.`))
193
+ process.exit(1);
194
+ }
195
+
196
+ const rPackage = JSON.parse(retrievedPackageData);
197
+
198
+
199
+ const componentInfoQuestions = await getComponentInfoQuestions(rPackage, configDef.library);
200
+ let componentInfo = await inquirer.prompt(componentInfoQuestions);
201
+
202
+ const currentDate = new Date(Date.now());
203
+ const dateStamp = currentDate.toString();
204
+ const timeStamp = currentDate.toJSON();
205
+
206
+ componentInfo = { ...componentInfo, dependencies: rPackage.dependencies, buildDate: dateStamp, timeStamp };
207
+
208
+ console.log("\nBuilding RC...");
209
+
210
+ const sDevBuild = devBuild ? '(dev build)' : '';
211
+ const tasks = new Listr(
212
+ [
213
+ {
214
+ title: 'Validate config schema',
215
+ task: async () => {
216
+ await validate(componentKey);
217
+ }
218
+ },
219
+ {
220
+ title: 'Lint component',
221
+ task: async () => {
222
+ const targetDirectory = await getComponentDirectoryPath(componentKey);
223
+ // console.log(`in buildComponent Lint component task: componentKey: ${componentKey} targetDirectory: ${targetDirectory}`);
224
+ await lintComponent(targetDirectory);
225
+ }
226
+ },
227
+ {
228
+ title: `Bundle Component ${sDevBuild}`,
229
+ task: async () => bundleComponent(componentKey, defaultPegaConfig.sourceMap, devBuild),
230
+ skip: () => (options.skipBundle ? 'Skipped bundling component' : undefined)
231
+ },
232
+ {
233
+ title: 'Create internal zip of component',
234
+ task: async () => {
235
+ const output = await zipComponent(componentKey);
236
+ content = { ...content, ...output, componentInfo: componentInfo };
237
+ }
238
+ },
239
+ {
240
+ title: 'Create zip file',
241
+ task: async () => {
242
+ const output = await zipRC(content, exportPath, archiveFileName);
243
+ content = { ...content, ...output };
244
+ }
245
+ }
246
+ ],
247
+ {
248
+ exitOnError: true
249
+ }
250
+ );
251
+
252
+ await tasks.run().catch(err => {
253
+ console.log(chalk.bold.red(err.toString()));
254
+ process.exit(1);
255
+ });
256
+
257
+ console.log(`\nRuntime component ${chalk.green(`${archiveFileName}`)} has been created in ${chalk.green(`${exportPath}`)}.\n`);
258
+
259
+ }
260
+ else {
261
+ console.log(chalk.bold.red(`\nNo components to export.\n`));
262
+ }
263
+
264
+ }
265
+
266
+ addDebugLog("export RC", "END", "-");
267
+
268
+ };
@@ -28,6 +28,7 @@ export { default as setPermanent } from './set-perm/index.js';
28
28
  export { default as storeComponents } from './store/index.js';
29
29
  export { default as importArchive } from './import-archive/index.js';
30
30
  export { default as exportArchive } from './export-archive/index.js';
31
+ export { default as exportRC } from './export-rc/index.js';
31
32
  export { default as importComponent } from './import-comp/index.js';
32
33
  export { default as updateComponent } from './update/index.js';
33
34
  export { default as validate } from './validator/index.js';
@@ -25,6 +25,7 @@
25
25
  "delete": "custom-dx-components delete",
26
26
  "deleteAll": "custom-dx-components deleteAll",
27
27
  "importComponent": "custom-dx-components importComponent",
28
+ "exportRC" : "custom-dx-components exportRC",
28
29
  "help": "custom-dx-components showHelp",
29
30
  "publish": "custom-dx-components publish",
30
31
  "_***Component_mode_only***": "Component only commands",
@@ -107,7 +108,7 @@
107
108
  "@babel/preset-react": "^7.27.1",
108
109
  "@babel/preset-typescript": "^7.27.1",
109
110
  "@pega/configs": "^0.16.3",
110
- "@pega/custom-dx-components": "~25.1.13",
111
+ "@pega/custom-dx-components": "~25.1.15",
111
112
  "@pega/pcore-pconnect-typedefs": "^4.1.0",
112
113
  "@storybook/addon-actions": "^8.6.14",
113
114
  "@storybook/addon-essentials": "^8.6.14",
@@ -11,14 +11,17 @@ import {
11
11
  import {
12
12
  getComponentDirectoryPath,
13
13
  getComponents,
14
- getOOTBComponents,
15
14
  isComponentValidLength,
16
- addDebugLog } from '../../util.js';
15
+ getConfigDefaults,
16
+ getLibraryBasedCL } from '../../util.js';
17
17
 
18
18
  const ajv = new Ajv({ allErrors: true });
19
19
 
20
20
  const combinationTypes = ['PAGE', 'CASE'];
21
21
 
22
+ const configDef = getConfigDefaults();
23
+ const isLibraryBasedCL = getLibraryBasedCL();
24
+
22
25
  const allowedSubtypes = [
23
26
  ...new Set([
24
27
  ...COMPONENT_SCHEMA.subtype.field.map(({ value }) => value),
@@ -343,6 +346,14 @@ export default async (option) => {
343
346
  throw new Error(`Invalid: componentName is too long.`);
344
347
  }
345
348
 
349
+ if (isLibraryBasedCL) {
350
+ if (componentToValidate.indexOf(configDef.currentOrgLib + "_") != 0) {
351
+ const arPrefix = componentToValidate.split("_");
352
+ const sPrefix = `${arPrefix[0]}_${arPrefix[1]}`;
353
+ throw new Error(`Component: ${componentToValidate} prefix invalid.\n --> '${sPrefix}' prefix does not match configuration '${configDef.currentOrgLib}'. \n --> Org_Lib prefix of component must match Organization and Library values \n --> in package.json and tasks.config.json respectively.`);
354
+ }
355
+ }
356
+
346
357
  const componentDirectory = await getComponentDirectoryPath(componentToValidate);
347
358
  let configFileName = "config.json";
348
359
 
@@ -393,8 +404,29 @@ export default async (option) => {
393
404
  process.exit(1);
394
405
  }
395
406
 
407
+ if (isLibraryBasedCL) {
408
+ if (data.library !== configDef.library) {
409
+ throw new Error (`${chalk.redBright(`Error: Invalid library in config.json for component '${data.name}'.\n -->Library: '${data.library}' must match tasks.config library: '${configDef.library}'.`)}`);
410
+ }
411
+
412
+ if (data.organization !== configDef.organization) {
413
+ throw new Error (`${chalk.redBright(`Error: Invalid organization in config.json for component '${data.name}'.\n -->Organization: '${data.organization}' must match package.json organization: '${configDef.organization}'.`)}`);
414
+ }
415
+
416
+ if (data.componentKey.indexOf(configDef.currentOrgLib + "_") !== 0) {
417
+ const arPrefix = data.componentKey.split("_");
418
+ const sPrefix = `${arPrefix[0]}_${arPrefix[1]}`
419
+ throw new Error (`${chalk.redBright(`Error: Invalid componentKey in config.json for component '${data.name}'.\n --> Component prefix Org_Lib: '${sPrefix}' must match '${configDef.currentOrgLib}'. \n --> These represent organization and library values found in package.json and tasks.config.json respectively. `)}`);
420
+ }
421
+
422
+ }
423
+
396
424
  console.log(`${chalk.bold.green(componentToValidate)} schema is valid`);
397
425
  } else {
398
426
  throw new Error(`Invalid: ${ajv.errorsText(validateSchema.errors)}`);
399
427
  }
428
+
429
+
430
+
431
+
400
432
  };
package/src/util.js CHANGED
@@ -1327,15 +1327,17 @@ export const showVersion = async () => {
1327
1327
  // get package version, so can display at start
1328
1328
  // const require = createRequire(import.meta.url);
1329
1329
  const require = createRequire(currentPath.default);
1330
- const sLibMode = getLibraryBased() ? "(library mode)" : "";
1330
+ const serverType = getServerType();
1331
+ const sLibMode = getLibraryBased() ? "(library mode)" : serverType === "launchpad" ? "(LaunchPad)" : "";
1331
1332
 
1332
1333
  const pData = require('@pega/custom-dx-components/package.json');
1333
1334
  const serverConfig = await getPegaServerConfig();
1334
1335
 
1336
+
1335
1337
  console.log(chalk.green(`DX Component Builder${sLibMode} v${pData.version}`));
1336
1338
  showDebug = getShowDebug();
1337
1339
 
1338
- if (getLibraryBased() && getServerType() === "launchpad") {
1340
+ if (getLibraryBased() && serverType === "launchpad") {
1339
1341
  console.log(`\nLaunchpad commands only supported for ${chalk.bold.green('non library mode')} components.\n`);
1340
1342
  process.exit();
1341
1343
  }
@@ -2848,6 +2850,11 @@ export const getStandaloneMaxPrefixCount = () => {
2848
2850
  }
2849
2851
 
2850
2852
  export const isComponentValidLength = (componentKey) => {
2853
+ const serverType = getServerType();
2854
+ if (serverType === "launchpad") {
2855
+ return true;
2856
+ }
2857
+
2851
2858
  const configDef = getConfigDefaults();
2852
2859
  const componentName = componentKey.replace(configDef.currentOrgLib + "_", "");
2853
2860
  if (componentName.length <= getMaxComponentNameCount()) {