@sitecore-content-sdk/cli 1.3.0-canary.4 → 1.3.0-canary.5

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.
@@ -49,17 +49,19 @@ exports.unitMocks = void 0;
49
49
  exports.args = args;
50
50
  exports.builder = builder;
51
51
  exports.handler = handler;
52
+ const fs_1 = __importDefault(require("fs"));
53
+ const path_1 = __importDefault(require("path"));
52
54
  const chalk_1 = __importDefault(require("chalk"));
53
55
  const child_process_1 = require("child_process");
54
56
  const tools = __importStar(require("@sitecore-content-sdk/core/tools"));
55
57
  const inquirer_1 = __importDefault(require("inquirer"));
56
- const load_config_1 = __importDefault(require("../../../utils/load-config"));
57
58
  const generate_map_1 = require("./generate-map");
58
- let { getComponentVariantSpec, getComponentList, getComponentVariantSpecUrl } = tools;
59
+ const load_config_1 = __importDefault(require("../../../utils/load-config"));
60
+ let { getComponentSpec, getComponentList, getComponentSpecUrl } = tools;
59
61
  const unitMocks = (toolsModule) => {
60
- getComponentVariantSpec = toolsModule.getComponentVariantSpec;
62
+ getComponentSpec = toolsModule.getComponentSpec;
61
63
  getComponentList = toolsModule.getComponentList;
62
- getComponentVariantSpecUrl = toolsModule.getComponentVariantSpecUrl;
64
+ getComponentSpecUrl = toolsModule.getComponentSpecUrl;
63
65
  };
64
66
  exports.unitMocks = unitMocks;
65
67
  /**
@@ -68,11 +70,11 @@ exports.unitMocks = unitMocks;
68
70
  function args(yargs) {
69
71
  /* istanbul ignore next */
70
72
  return yargs
71
- .positional('variantId', {
73
+ .positional('componentId', {
72
74
  requiresArg: true,
73
75
  positional: true,
74
76
  type: 'string',
75
- describe: `The unique identifier of the newly created variant.`,
77
+ describe: `The unique identifier of the newly created component.`,
76
78
  })
77
79
  .option('token', {
78
80
  requiresArg: true,
@@ -83,7 +85,7 @@ function args(yargs) {
83
85
  .option('target-path', {
84
86
  requiresArg: false,
85
87
  type: 'string',
86
- describe: 'The target path for the component variant.',
88
+ describe: 'The relative target path for the component.\n(Example: components/Promo.WithText.ts)',
87
89
  })
88
90
  .option('skip-component-map', {
89
91
  requiresArg: false,
@@ -108,16 +110,35 @@ function args(yargs) {
108
110
  */
109
111
  function builder(yargs) {
110
112
  /* istanbul ignore next */
111
- return yargs.command('add <variant-id>', 'Adds a component variant', args, handler);
113
+ return yargs.command('add <component-id>', 'Adds a component', args, handler);
112
114
  }
115
+ /**
116
+ * Validates the target path. If no target path is provided, returns true.
117
+ * @param {string} [targetPath] - The target path to validate.
118
+ * @returns {boolean | string} True if the target path is valid, the error message otherwise.
119
+ */
120
+ const validateTargetPath = (targetPath) => {
121
+ if (!targetPath) {
122
+ return true;
123
+ }
124
+ // Check for unsafe absolute path starting with "/"
125
+ if (path_1.default.isAbsolute(targetPath)) {
126
+ return 'Target path cannot be an absolute path starting with "/"';
127
+ }
128
+ // Check for path traversal attempts
129
+ if (targetPath.includes('..')) {
130
+ return 'Target path cannot contain ".." (path traversal)';
131
+ }
132
+ return true;
133
+ };
113
134
  /**
114
135
  * Handler for the add command.
115
136
  * @param {AddArgs} argv - The arguments passed to the command.
116
137
  */
117
138
  function handler(argv) {
118
139
  return __awaiter(this, void 0, void 0, function* () {
119
- const { variantId, token, targetPath: targetPathArg, skipComponentMap, config, overwrite } = argv;
120
- console.log(chalk_1.default.green('Adding component variant'));
140
+ const { componentId, token, targetPath: targetPathArg, skipComponentMap, config, overwrite, } = argv;
141
+ console.log(chalk_1.default.green('Adding component'));
121
142
  let targetPath = targetPathArg;
122
143
  const cliConfig = (0, load_config_1.default)(config);
123
144
  if (!cliConfig.config) {
@@ -125,10 +146,16 @@ function handler(argv) {
125
146
  return;
126
147
  }
127
148
  const { edgeUrl } = cliConfig.config.api.edge;
149
+ let backupPath;
150
+ let resolvedFilePath;
128
151
  try {
129
- const spec = yield getComponentVariantSpec({
152
+ if (validateTargetPath(targetPath) !== true) {
153
+ console.error(chalk_1.default.red(validateTargetPath(targetPath)));
154
+ return;
155
+ }
156
+ const spec = yield getComponentSpec({
130
157
  edgeUrl,
131
- variantId,
158
+ componentId,
132
159
  targetPath,
133
160
  token,
134
161
  });
@@ -155,29 +182,54 @@ function handler(argv) {
155
182
  type: 'input',
156
183
  name: 'targetPath',
157
184
  required: true,
158
- message: `Enter the target path for the component variant.\nThe filename must follow the format: {componentName}.{variantName}.{extension}\n(example: src/components/MyComponent/MyComponent.variantA.ts):`,
185
+ message: `Enter the target path for the component.\n(Example: components/Promo.WithText.ts):`,
186
+ validate: validateTargetPath,
159
187
  })
160
188
  .then((answer) => answer.targetPath);
161
189
  }
162
190
  }
163
- const variantSpecUrl = getComponentVariantSpecUrl({
164
- variantId,
191
+ resolvedFilePath = path_1.default.resolve(process.cwd(), targetPath);
192
+ const isFileAlreadyExists = fs_1.default.existsSync(resolvedFilePath);
193
+ backupPath = `${resolvedFilePath}.backup`;
194
+ if (isFileAlreadyExists) {
195
+ if (!overwrite) {
196
+ const shouldOverwrite = yield inquirer_1.default.prompt({
197
+ type: 'confirm',
198
+ name: 'overwrite',
199
+ message: `File already exists: ${targetPath}. Overwrite?`,
200
+ default: false,
201
+ });
202
+ if (!shouldOverwrite.overwrite) {
203
+ return;
204
+ }
205
+ }
206
+ fs_1.default.renameSync(resolvedFilePath, backupPath);
207
+ console.log(chalk_1.default.yellow(`Overwriting existing file: ${targetPath}`));
208
+ }
209
+ const componentSpecUrl = getComponentSpecUrl({
210
+ componentId,
165
211
  targetPath: targetPath,
166
212
  edgeUrl,
167
213
  token,
168
214
  });
169
- (0, child_process_1.execSync)(`npx shadcn@^3.4.2 add "${variantSpecUrl}"${overwrite ? ' --overwrite' : ''}`, {
215
+ (0, child_process_1.execSync)(`npx shadcn@^3.4.2 add "${componentSpecUrl}"`, {
170
216
  stdio: 'inherit',
171
217
  cwd: process.cwd(),
172
218
  });
219
+ if (isFileAlreadyExists) {
220
+ fs_1.default.unlinkSync(backupPath);
221
+ }
173
222
  if (!skipComponentMap) {
174
223
  (0, generate_map_1.handler)({ config });
175
224
  }
176
- console.log(chalk_1.default.green(`Component variant ${variantName} added successfully`));
225
+ console.log(chalk_1.default.green(`Component ${componentName} added successfully`));
177
226
  }
178
227
  catch (error) {
228
+ if (backupPath && resolvedFilePath && fs_1.default.existsSync(backupPath)) {
229
+ fs_1.default.renameSync(backupPath, resolvedFilePath);
230
+ }
179
231
  const errorMessage = error instanceof Error ? error.message : String(error);
180
- console.error(chalk_1.default.red(`Failed to add component variant: ${errorMessage}`));
232
+ console.error(chalk_1.default.red(`Failed to add component: ${errorMessage}`));
181
233
  return;
182
234
  }
183
235
  });
@@ -7,17 +7,19 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
+ import fs from 'fs';
11
+ import path from 'path';
10
12
  import chalk from 'chalk';
11
13
  import { execSync } from 'child_process';
12
14
  import * as tools from '@sitecore-content-sdk/core/tools';
13
15
  import inquirer from 'inquirer';
14
- import loadCliConfig from '../../../utils/load-config';
15
16
  import { handler as generateMapHandler } from './generate-map';
16
- let { getComponentVariantSpec, getComponentList, getComponentVariantSpecUrl } = tools;
17
+ import loadCliConfig from '../../../utils/load-config';
18
+ let { getComponentSpec, getComponentList, getComponentSpecUrl } = tools;
17
19
  export const unitMocks = (toolsModule) => {
18
- getComponentVariantSpec = toolsModule.getComponentVariantSpec;
20
+ getComponentSpec = toolsModule.getComponentSpec;
19
21
  getComponentList = toolsModule.getComponentList;
20
- getComponentVariantSpecUrl = toolsModule.getComponentVariantSpecUrl;
22
+ getComponentSpecUrl = toolsModule.getComponentSpecUrl;
21
23
  };
22
24
  /**
23
25
  * @param {Argv} yargs
@@ -25,11 +27,11 @@ export const unitMocks = (toolsModule) => {
25
27
  export function args(yargs) {
26
28
  /* istanbul ignore next */
27
29
  return yargs
28
- .positional('variantId', {
30
+ .positional('componentId', {
29
31
  requiresArg: true,
30
32
  positional: true,
31
33
  type: 'string',
32
- describe: `The unique identifier of the newly created variant.`,
34
+ describe: `The unique identifier of the newly created component.`,
33
35
  })
34
36
  .option('token', {
35
37
  requiresArg: true,
@@ -40,7 +42,7 @@ export function args(yargs) {
40
42
  .option('target-path', {
41
43
  requiresArg: false,
42
44
  type: 'string',
43
- describe: 'The target path for the component variant.',
45
+ describe: 'The relative target path for the component.\n(Example: components/Promo.WithText.ts)',
44
46
  })
45
47
  .option('skip-component-map', {
46
48
  requiresArg: false,
@@ -65,16 +67,35 @@ export function args(yargs) {
65
67
  */
66
68
  export function builder(yargs) {
67
69
  /* istanbul ignore next */
68
- return yargs.command('add <variant-id>', 'Adds a component variant', args, handler);
70
+ return yargs.command('add <component-id>', 'Adds a component', args, handler);
69
71
  }
72
+ /**
73
+ * Validates the target path. If no target path is provided, returns true.
74
+ * @param {string} [targetPath] - The target path to validate.
75
+ * @returns {boolean | string} True if the target path is valid, the error message otherwise.
76
+ */
77
+ const validateTargetPath = (targetPath) => {
78
+ if (!targetPath) {
79
+ return true;
80
+ }
81
+ // Check for unsafe absolute path starting with "/"
82
+ if (path.isAbsolute(targetPath)) {
83
+ return 'Target path cannot be an absolute path starting with "/"';
84
+ }
85
+ // Check for path traversal attempts
86
+ if (targetPath.includes('..')) {
87
+ return 'Target path cannot contain ".." (path traversal)';
88
+ }
89
+ return true;
90
+ };
70
91
  /**
71
92
  * Handler for the add command.
72
93
  * @param {AddArgs} argv - The arguments passed to the command.
73
94
  */
74
95
  export function handler(argv) {
75
96
  return __awaiter(this, void 0, void 0, function* () {
76
- const { variantId, token, targetPath: targetPathArg, skipComponentMap, config, overwrite } = argv;
77
- console.log(chalk.green('Adding component variant'));
97
+ const { componentId, token, targetPath: targetPathArg, skipComponentMap, config, overwrite, } = argv;
98
+ console.log(chalk.green('Adding component'));
78
99
  let targetPath = targetPathArg;
79
100
  const cliConfig = loadCliConfig(config);
80
101
  if (!cliConfig.config) {
@@ -82,10 +103,16 @@ export function handler(argv) {
82
103
  return;
83
104
  }
84
105
  const { edgeUrl } = cliConfig.config.api.edge;
106
+ let backupPath;
107
+ let resolvedFilePath;
85
108
  try {
86
- const spec = yield getComponentVariantSpec({
109
+ if (validateTargetPath(targetPath) !== true) {
110
+ console.error(chalk.red(validateTargetPath(targetPath)));
111
+ return;
112
+ }
113
+ const spec = yield getComponentSpec({
87
114
  edgeUrl,
88
- variantId,
115
+ componentId,
89
116
  targetPath,
90
117
  token,
91
118
  });
@@ -112,29 +139,54 @@ export function handler(argv) {
112
139
  type: 'input',
113
140
  name: 'targetPath',
114
141
  required: true,
115
- message: `Enter the target path for the component variant.\nThe filename must follow the format: {componentName}.{variantName}.{extension}\n(example: src/components/MyComponent/MyComponent.variantA.ts):`,
142
+ message: `Enter the target path for the component.\n(Example: components/Promo.WithText.ts):`,
143
+ validate: validateTargetPath,
116
144
  })
117
145
  .then((answer) => answer.targetPath);
118
146
  }
119
147
  }
120
- const variantSpecUrl = getComponentVariantSpecUrl({
121
- variantId,
148
+ resolvedFilePath = path.resolve(process.cwd(), targetPath);
149
+ const isFileAlreadyExists = fs.existsSync(resolvedFilePath);
150
+ backupPath = `${resolvedFilePath}.backup`;
151
+ if (isFileAlreadyExists) {
152
+ if (!overwrite) {
153
+ const shouldOverwrite = yield inquirer.prompt({
154
+ type: 'confirm',
155
+ name: 'overwrite',
156
+ message: `File already exists: ${targetPath}. Overwrite?`,
157
+ default: false,
158
+ });
159
+ if (!shouldOverwrite.overwrite) {
160
+ return;
161
+ }
162
+ }
163
+ fs.renameSync(resolvedFilePath, backupPath);
164
+ console.log(chalk.yellow(`Overwriting existing file: ${targetPath}`));
165
+ }
166
+ const componentSpecUrl = getComponentSpecUrl({
167
+ componentId,
122
168
  targetPath: targetPath,
123
169
  edgeUrl,
124
170
  token,
125
171
  });
126
- execSync(`npx shadcn@^3.4.2 add "${variantSpecUrl}"${overwrite ? ' --overwrite' : ''}`, {
172
+ execSync(`npx shadcn@^3.4.2 add "${componentSpecUrl}"`, {
127
173
  stdio: 'inherit',
128
174
  cwd: process.cwd(),
129
175
  });
176
+ if (isFileAlreadyExists) {
177
+ fs.unlinkSync(backupPath);
178
+ }
130
179
  if (!skipComponentMap) {
131
180
  generateMapHandler({ config });
132
181
  }
133
- console.log(chalk.green(`Component variant ${variantName} added successfully`));
182
+ console.log(chalk.green(`Component ${componentName} added successfully`));
134
183
  }
135
184
  catch (error) {
185
+ if (backupPath && resolvedFilePath && fs.existsSync(backupPath)) {
186
+ fs.renameSync(backupPath, resolvedFilePath);
187
+ }
136
188
  const errorMessage = error instanceof Error ? error.message : String(error);
137
- console.error(chalk.red(`Failed to add component variant: ${errorMessage}`));
189
+ console.error(chalk.red(`Failed to add component: ${errorMessage}`));
138
190
  return;
139
191
  }
140
192
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sitecore-content-sdk/cli",
3
- "version": "1.3.0-canary.4",
3
+ "version": "1.3.0-canary.5",
4
4
  "description": "Sitecore Content SDK CLI",
5
5
  "main": "dist/cjs/cli.js",
6
6
  "module": "dist/esm/cli.js",
@@ -34,7 +34,7 @@
34
34
  "url": "https://github.com/sitecore/content-sdk/issues"
35
35
  },
36
36
  "dependencies": {
37
- "@sitecore-content-sdk/core": "1.3.0-canary.4",
37
+ "@sitecore-content-sdk/core": "1.3.0-canary.5",
38
38
  "chokidar": "^4.0.3",
39
39
  "dotenv": "^16.5.0",
40
40
  "dotenv-expand": "^12.0.2",
@@ -71,7 +71,7 @@
71
71
  "ts-node": "^10.9.1",
72
72
  "typescript": "~5.8.3"
73
73
  },
74
- "gitHead": "24542709986dbcaba7706fe86ca51cf6b64fd282",
74
+ "gitHead": "654b9fe883af77f4bbab49114002ac0cf2054125",
75
75
  "files": [
76
76
  "dist",
77
77
  "types"
@@ -1,17 +1,17 @@
1
1
  import { Argv } from 'yargs';
2
2
  import * as tools from '@sitecore-content-sdk/core/tools';
3
- export declare const unitMocks: (toolsModule: Pick<typeof tools, "getComponentVariantSpec" | "getComponentList" | "getComponentVariantSpecUrl">) => void;
3
+ export declare const unitMocks: (toolsModule: Pick<typeof tools, "getComponentSpec" | "getComponentList" | "getComponentSpecUrl">) => void;
4
4
  type AddArgs = {
5
5
  /**
6
- * The unique identifier of the newly created variant.
6
+ * The unique identifier of the newly created component.
7
7
  */
8
- variantId: string;
8
+ componentId: string;
9
9
  /**
10
10
  * The authentication token.
11
11
  */
12
12
  token: string;
13
13
  /**
14
- * The target path for the component variant.
14
+ * The target path for the component.
15
15
  */
16
16
  targetPath?: string;
17
17
  /**
@@ -33,8 +33,8 @@ type AddArgs = {
33
33
  /**
34
34
  * @param {Argv} yargs
35
35
  */
36
- export declare function args(yargs: Argv<AddArgs>): Argv<import("yargs").Omit<import("yargs").Omit<import("yargs").Omit<import("yargs").Omit<AddArgs, "variantId"> & {
37
- variantId: string | undefined;
36
+ export declare function args(yargs: Argv<AddArgs>): Argv<import("yargs").Omit<import("yargs").Omit<import("yargs").Omit<import("yargs").Omit<AddArgs, "componentId"> & {
37
+ componentId: string | undefined;
38
38
  }, "token"> & {
39
39
  token: string;
40
40
  } & {