@contentstack/cli-migration 1.5.5 → 1.6.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 CHANGED
@@ -21,7 +21,7 @@ $ npm install -g @contentstack/cli-migration
21
21
  $ csdx COMMAND
22
22
  running command...
23
23
  $ csdx (--version)
24
- @contentstack/cli-migration/1.5.5 darwin-arm64 node-v21.6.2
24
+ @contentstack/cli-migration/1.6.0 darwin-arm64 node-v22.2.0
25
25
  $ csdx --help [COMMAND]
26
26
  USAGE
27
27
  $ csdx COMMAND
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@contentstack/cli-migration",
3
- "version": "1.5.5",
3
+ "version": "1.6.0",
4
4
  "author": "@contentstack",
5
5
  "bugs": "https://github.com/contentstack/cli/issues",
6
6
  "dependencies": {
7
- "@contentstack/cli-command": "~1.2.18",
8
- "@contentstack/cli-utilities": "~1.6.1",
7
+ "@contentstack/cli-command": "~1.2.19",
8
+ "@contentstack/cli-utilities": "~1.7.0",
9
9
  "async": "^3.2.4",
10
10
  "callsites": "^3.1.0",
11
11
  "cardinal": "^2.1.1",
@@ -66,4 +66,4 @@
66
66
  "cm:migration": "O-MGRTN"
67
67
  }
68
68
  }
69
- }
69
+ }
@@ -19,12 +19,13 @@ const {
19
19
  flags,
20
20
  isAuthenticated,
21
21
  pathValidator,
22
+ sanitizePath,
22
23
  } = require('@contentstack/cli-utilities');
23
24
 
24
25
  const { ApiError, SchemaValidator, MigrationError, FieldValidator } = require('../../../validators');
25
26
 
26
27
  // Utils
27
- const { map: _map, constants, safePromise, errorHelper } = require('../../../utils');
28
+ const { map: _map, constants, safePromise, errorHelper, installModules } = require('../../../utils');
28
29
  // Properties
29
30
  const { get, set, getMapInstance, resetMapInstance } = _map;
30
31
  const {
@@ -66,7 +67,7 @@ class MigrationCommand extends Command {
66
67
  this.exit();
67
68
  }
68
69
 
69
- if (!filePath) {
70
+ if (!filePath || !fs.existsSync(filePath)) {
70
71
  this.log('Please provide the migration script file path, use --file-path flag');
71
72
  this.exit();
72
73
  }
@@ -128,6 +129,11 @@ class MigrationCommand extends Command {
128
129
  set(MANAGEMENT_SDK, mapInstance, stackSDKInstance);
129
130
  set(MANAGEMENT_CLIENT, mapInstance, APIClient);
130
131
 
132
+ if (!(await installModules(filePath, multi))) {
133
+ this.log(`Error: Failed to install dependencies for the specified scripts.`);
134
+ process.exit(1);
135
+ }
136
+
131
137
  if (multi) {
132
138
  await this.execMultiFiles(filePath, mapInstance);
133
139
  } else {
@@ -180,7 +186,7 @@ class MigrationCommand extends Command {
180
186
  const file = element;
181
187
  if (extname(file) === '.js') {
182
188
  // eslint-disable-next-line no-await-in-loop
183
- await this.execSingleFile(pathValidator(resolve(filePath, file)), mapInstance);
189
+ await this.execSingleFile(pathValidator(resolve(sanitizePath(filePath), sanitizePath(file))), mapInstance);
184
190
  }
185
191
  }
186
192
  } catch (error) {
@@ -2,10 +2,10 @@
2
2
 
3
3
  const getCallsites = require('callsites');
4
4
  const { parse, resolve } = require('path');
5
- const { pathValidator } = require('@contentstack/cli-utilities');
5
+ const { pathValidator, sanitizePath } = require('@contentstack/cli-utilities');
6
6
 
7
7
  function getFileDirectory(path) {
8
- const parentPath = pathValidator(resolve(path, '../')); // Assuming that will be 2 folders up
8
+ const parentPath = pathValidator(resolve(sanitizePath(path), '../')); // Assuming that will be 2 folders up
9
9
  return parse(parentPath).dir;
10
10
  }
11
11
 
@@ -17,4 +17,5 @@ module.exports = {
17
17
  getBatches: require('./get-batches'),
18
18
  autoRetry: require('./auto-retry'),
19
19
  contentstackSdk: require('./contentstack-sdk'),
20
+ installModules: require('./modules'),
20
21
  };
@@ -4,7 +4,7 @@ const { createLogger, format, transports } = require('winston');
4
4
  const { resolve, join } = require('path');
5
5
  const { slice } = Array.prototype;
6
6
  const { stringify } = JSON;
7
- const { pathValidator } = require('@contentstack/cli-utilities');
7
+ const { pathValidator, sanitizePath } = require('@contentstack/cli-utilities');
8
8
 
9
9
  const { combine, label, printf, colorize } = format;
10
10
 
@@ -33,7 +33,7 @@ function init(logFileName) {
33
33
  // Create dir if does not exist
34
34
  makeDir(logsDir);
35
35
 
36
- const logPath = pathValidator(join(logsDir, logFileName + '.log'));
36
+ const logPath = pathValidator(join(sanitizePath(logsDir), sanitizePath(logFileName) + '.log'));
37
37
  const logger = createLogger({
38
38
  format: combine(colorize(), label({ label: 'Migration' }), customFormat),
39
39
  transports: [new transports.File({ filename: logPath })],
@@ -1,14 +1,15 @@
1
1
  const winston = require('winston');
2
2
  const path = require('path');
3
+ const { sanitizePath } = require('@contentstack/cli-utilities');
3
4
  module.exports = class MigrationLogger {
4
5
  constructor(filePath) {
5
- this.filePath = path.join(filePath, 'migration-logs');
6
+ this.filePath = path.join(sanitizePath(filePath), 'migration-logs');
6
7
  this.logger = winston.createLogger({
7
8
  levels: { error: 1 },
8
9
  transports: [
9
10
  new winston.transports.File({
10
11
  level: 'error',
11
- filename: path.join(this.filePath, 'error.logs'),
12
+ filename: path.join(sanitizePath(this.filePath), 'error.logs'),
12
13
  format: winston.format.combine(winston.format.timestamp(), winston.format.json()),
13
14
  }),
14
15
  ],
@@ -0,0 +1,134 @@
1
+ const fs = require('fs');
2
+ const { execSync } = require('child_process');
3
+ const path = require('path');
4
+ const { sanitizePath } = require('@contentstack/cli-utilities');
5
+ const os = require('os');
6
+ const { builtinModules } = require('module');
7
+
8
+ const internalModules = new Set(builtinModules);
9
+
10
+ function checkWritePermissionToDirectory(directory) {
11
+ try {
12
+ fs.accessSync(directory, fs.constants.W_OK);
13
+ return true;
14
+ } catch (err) {
15
+ console.log(`Permission Denied! You do not have the necessary write access for this directory.`);
16
+ return false;
17
+ }
18
+ }
19
+
20
+ function doesPackageJsonExist(directory) {
21
+ return fs.existsSync(path.join(sanitizePath(directory), 'package.json'));
22
+ }
23
+
24
+ function scanDirectory(directory) {
25
+ return fs.readdirSync(directory);
26
+ }
27
+
28
+ function scanFileForDependencies(directory, files) {
29
+ const dependencies = new Set();
30
+
31
+ files.forEach((file) => {
32
+ const filePath = path.join(sanitizePath(directory), sanitizePath(file));
33
+ if (path.extname(filePath) === '.js') {
34
+ const fileContent = fs.readFileSync(filePath, 'utf-8');
35
+ findModulesSync(fileContent).forEach((dep) => dependencies.add(dep));
36
+ }
37
+ });
38
+
39
+ return [...dependencies];
40
+ }
41
+
42
+ function createPackageJson(directory) {
43
+ const templateString = `{
44
+ "name": "MigrationPackage",
45
+ "version": "1.0.0",
46
+ "main": "",
47
+ "scripts": {},
48
+ "keywords": [],
49
+ "author": "",
50
+ "license": "ISC",
51
+ "description": ""
52
+ }`;
53
+
54
+ fs.writeFileSync(path.join(sanitizePath(directory), 'package.json'), templateString);
55
+ }
56
+
57
+ function installDependencies(dependencies, directory) {
58
+ const installedDependencies = new Set();
59
+
60
+ dependencies.forEach((dep) => {
61
+ if (!internalModules.has(dep)) {
62
+ const pkg = dep.startsWith('@') ? dep : dep.split('/')[0];
63
+ if (!installedDependencies.has(pkg)) {
64
+ executeShellCommand(`npm i ${pkg}`, directory);
65
+ installedDependencies.add(pkg);
66
+ }
67
+ }
68
+ });
69
+ }
70
+
71
+ function executeShellCommand(command, directory = '') {
72
+ try {
73
+ execSync(command, { stdio: 'inherit', cwd: directory });
74
+ console.log(`The '${command}' command has been executed successfully.`);
75
+ } catch (error) {
76
+ console.error(`Command execution failed. Error: ${error.message}`);
77
+ }
78
+ }
79
+
80
+ async function installModules(filePath, multiple) {
81
+ const files = multiple ? [] : [path.basename(filePath)];
82
+ const dirPath = multiple ? filePath : path.dirname(filePath);
83
+
84
+ if (checkWritePermissionToDirectory(dirPath)) {
85
+ if (multiple) {
86
+ files.push(...scanDirectory(dirPath));
87
+ }
88
+
89
+ if (files.length === 0) {
90
+ console.log(`Error: Could not locate files needed to create package.json. Exiting the process.`);
91
+ return true;
92
+ }
93
+
94
+ const dependencies = scanFileForDependencies(dirPath, files);
95
+
96
+ if (!doesPackageJsonExist(dirPath)) {
97
+ console.log(`package.json not found. Creating a new package.json...`);
98
+ createPackageJson(dirPath);
99
+ }
100
+
101
+ installDependencies(dependencies, dirPath);
102
+ } else {
103
+ console.log(`You don't have write permission to the directory`);
104
+ return false;
105
+ }
106
+
107
+ console.log(`All dependencies installed successfully.`);
108
+ return true;
109
+ }
110
+
111
+ function findModulesSync(data) {
112
+ try {
113
+ const requireRegex = /require\(['"`](.*?)['"`]\)/g;
114
+ const importRegex = /import\s+(?:(?:[\w*\s{},]*)\s+from\s+)?['"`](.*?)['"`]/g;
115
+
116
+ const modules = new Set();
117
+ let match;
118
+
119
+ while ((match = requireRegex.exec(data)) !== null) {
120
+ modules.add(match[1]);
121
+ }
122
+
123
+ while ((match = importRegex.exec(data)) !== null) {
124
+ modules.add(match[1]);
125
+ }
126
+
127
+ return [...modules];
128
+ } catch (error) {
129
+ console.error(`Error reading file: ${error.message}`);
130
+ return [];
131
+ }
132
+ }
133
+
134
+ module.exports = installModules;