@strapi/cloud-cli 4.25.2 → 4.25.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,12 +1,12 @@
1
1
  import crypto$1 from "crypto";
2
- import fse from "fs-extra";
2
+ import * as fse from "fs-extra";
3
+ import fse__default from "fs-extra";
3
4
  import * as path from "path";
4
5
  import path__default from "path";
5
6
  import chalk from "chalk";
6
7
  import axios, { AxiosError } from "axios";
7
8
  import * as crypto from "node:crypto";
8
9
  import { env } from "@strapi/utils";
9
- import * as fs from "fs";
10
10
  import * as tar from "tar";
11
11
  import { minimatch } from "minimatch";
12
12
  import inquirer from "inquirer";
@@ -18,10 +18,10 @@ import jwt from "jsonwebtoken";
18
18
  import stringify from "fast-safe-stringify";
19
19
  import ora from "ora";
20
20
  import * as cliProgress from "cli-progress";
21
- import EventSource from "eventsource";
22
- import fs$1 from "fs/promises";
23
21
  import pkgUp from "pkg-up";
24
22
  import * as yup from "yup";
23
+ import _ from "lodash";
24
+ import EventSource from "eventsource";
25
25
  const apiConfig = {
26
26
  apiBaseUrl: env("STRAPI_CLI_CLOUD_API", "https://cloud-cli-api.strapi.io"),
27
27
  dashboardBaseUrl: env("STRAPI_CLI_CLOUD_DASHBOARD", "https://cloud.strapi.io")
@@ -40,23 +40,6 @@ const IGNORED_PATTERNS = [
40
40
  "**/.idea/**",
41
41
  "**/.vscode/**"
42
42
  ];
43
- const getFiles = (dirPath, ignorePatterns = [], arrayOfFiles = [], subfolder = "") => {
44
- const entries = fs.readdirSync(path.join(dirPath, subfolder));
45
- entries.forEach((entry) => {
46
- const entryPathFromRoot = path.join(subfolder, entry);
47
- const entryPath = path.relative(dirPath, entryPathFromRoot);
48
- const isIgnored = isIgnoredFile(dirPath, entryPathFromRoot, ignorePatterns);
49
- if (isIgnored) {
50
- return;
51
- }
52
- if (fs.statSync(entryPath).isDirectory()) {
53
- getFiles(dirPath, ignorePatterns, arrayOfFiles, entryPathFromRoot);
54
- } else {
55
- arrayOfFiles.push(entryPath);
56
- }
57
- });
58
- return arrayOfFiles;
59
- };
60
43
  const isIgnoredFile = (folderPath, file, ignorePatterns) => {
61
44
  ignorePatterns.push(...IGNORED_PATTERNS);
62
45
  const relativeFilePath = path.join(folderPath, file);
@@ -74,16 +57,35 @@ const isIgnoredFile = (folderPath, file, ignorePatterns) => {
74
57
  }
75
58
  return isIgnored;
76
59
  };
77
- const readGitignore = (folderPath) => {
60
+ const getFiles = async (dirPath, ignorePatterns = [], subfolder = "") => {
61
+ const arrayOfFiles = [];
62
+ const entries = await fse.readdir(path.join(dirPath, subfolder));
63
+ for (const entry of entries) {
64
+ const entryPathFromRoot = path.join(subfolder, entry);
65
+ const entryPath = path.relative(dirPath, entryPathFromRoot);
66
+ const isIgnored = isIgnoredFile(dirPath, entryPathFromRoot, ignorePatterns);
67
+ if (!isIgnored) {
68
+ if (fse.statSync(entryPath).isDirectory()) {
69
+ const subFiles = await getFiles(dirPath, ignorePatterns, entryPathFromRoot);
70
+ arrayOfFiles.push(...subFiles);
71
+ } else {
72
+ arrayOfFiles.push(entryPath);
73
+ }
74
+ }
75
+ }
76
+ return arrayOfFiles;
77
+ };
78
+ const readGitignore = async (folderPath) => {
78
79
  const gitignorePath = path.resolve(folderPath, ".gitignore");
79
- if (!fs.existsSync(gitignorePath))
80
+ const pathExist = await fse.pathExists(gitignorePath);
81
+ if (!pathExist)
80
82
  return [];
81
- const gitignoreContent = fs.readFileSync(gitignorePath, "utf8");
83
+ const gitignoreContent = await fse.readFile(gitignorePath, "utf8");
82
84
  return gitignoreContent.split(/\r?\n/).filter((line) => Boolean(line.trim()) && !line.startsWith("#"));
83
85
  };
84
86
  const compressFilesToTar = async (storagePath, folderToCompress, filename) => {
85
- const ignorePatterns = readGitignore(folderToCompress);
86
- const filesToCompress = getFiles(folderToCompress, ignorePatterns);
87
+ const ignorePatterns = await readGitignore(folderToCompress);
88
+ const filesToCompress = await getFiles(folderToCompress, ignorePatterns);
87
89
  return tar.c(
88
90
  {
89
91
  gzip: true,
@@ -96,7 +98,7 @@ const APP_FOLDER_NAME = "com.strapi.cli";
96
98
  const CONFIG_FILENAME = "config.json";
97
99
  async function checkDirectoryExists(directoryPath) {
98
100
  try {
99
- const fsStat = await fse.lstat(directoryPath);
101
+ const fsStat = await fse__default.lstat(directoryPath);
100
102
  return fsStat.isDirectory();
101
103
  } catch (e) {
102
104
  return false;
@@ -104,14 +106,14 @@ async function checkDirectoryExists(directoryPath) {
104
106
  }
105
107
  async function getTmpStoragePath() {
106
108
  const storagePath = path__default.join(os.tmpdir(), APP_FOLDER_NAME);
107
- await fse.ensureDir(storagePath);
109
+ await fse__default.ensureDir(storagePath);
108
110
  return storagePath;
109
111
  }
110
112
  async function getConfigPath() {
111
113
  const configDirs = XDGAppPaths(APP_FOLDER_NAME).configDirs();
112
114
  const configPath = configDirs.find(checkDirectoryExists);
113
115
  if (!configPath) {
114
- await fse.ensureDir(configDirs[0]);
116
+ await fse__default.ensureDir(configDirs[0]);
115
117
  return configDirs[0];
116
118
  }
117
119
  return configPath;
@@ -119,9 +121,9 @@ async function getConfigPath() {
119
121
  async function getLocalConfig() {
120
122
  const configPath = await getConfigPath();
121
123
  const configFilePath = path__default.join(configPath, CONFIG_FILENAME);
122
- await fse.ensureFile(configFilePath);
124
+ await fse__default.ensureFile(configFilePath);
123
125
  try {
124
- return await fse.readJSON(configFilePath, { encoding: "utf8", throws: true });
126
+ return await fse__default.readJSON(configFilePath, { encoding: "utf8", throws: true });
125
127
  } catch (e) {
126
128
  return {};
127
129
  }
@@ -129,10 +131,10 @@ async function getLocalConfig() {
129
131
  async function saveLocalConfig(data) {
130
132
  const configPath = await getConfigPath();
131
133
  const configFilePath = path__default.join(configPath, CONFIG_FILENAME);
132
- await fse.writeJson(configFilePath, data, { encoding: "utf8", spaces: 2, mode: 384 });
134
+ await fse__default.writeJson(configFilePath, data, { encoding: "utf8", spaces: 2, mode: 384 });
133
135
  }
134
136
  const name = "@strapi/cloud-cli";
135
- const version = "4.25.1";
137
+ const version = "4.25.3";
136
138
  const description = "Commands to interact with the Strapi Cloud";
137
139
  const keywords = [
138
140
  "strapi",
@@ -173,10 +175,11 @@ const scripts = {
173
175
  build: "pack-up build",
174
176
  clean: "run -T rimraf ./dist",
175
177
  lint: "run -T eslint .",
178
+ "test:unit": "run -T jest",
176
179
  watch: "pack-up watch"
177
180
  };
178
181
  const dependencies = {
179
- "@strapi/utils": "4.25.1",
182
+ "@strapi/utils": "4.25.3",
180
183
  axios: "1.6.0",
181
184
  chalk: "4.1.2",
182
185
  "cli-progress": "3.12.0",
@@ -201,8 +204,8 @@ const devDependencies = {
201
204
  "@types/cli-progress": "3.11.5",
202
205
  "@types/eventsource": "1.1.15",
203
206
  "@types/lodash": "^4.14.191",
204
- "eslint-config-custom": "4.25.1",
205
- tsconfig: "4.25.1"
207
+ "eslint-config-custom": "4.25.3",
208
+ tsconfig: "4.25.3"
206
209
  };
207
210
  const engines = {
208
211
  node: ">=18.0.0 <=20.x.x",
@@ -255,7 +258,7 @@ async function cloudApiFactory({ logger }, token) {
255
258
  deploy({ filePath, project }, { onUploadProgress }) {
256
259
  return axiosCloudAPI.post(
257
260
  `/deploy/${project.name}`,
258
- { file: fse.createReadStream(filePath) },
261
+ { file: fse__default.createReadStream(filePath) },
259
262
  {
260
263
  headers: {
261
264
  "Content-Type": "multipart/form-data"
@@ -298,8 +301,19 @@ async function cloudApiFactory({ logger }, token) {
298
301
  throw error;
299
302
  }
300
303
  },
301
- listProjects() {
302
- return axiosCloudAPI.get("/projects");
304
+ async listProjects() {
305
+ try {
306
+ const response = await axiosCloudAPI.get("/projects");
307
+ if (response.status !== 200) {
308
+ throw new Error("Error fetching cloud projects from the server.");
309
+ }
310
+ return response;
311
+ } catch (error) {
312
+ logger.debug(
313
+ "🥲 Oops! Couldn't retrieve your project's list from the server. Please try again."
314
+ );
315
+ throw error;
316
+ }
303
317
  },
304
318
  track(event, payload = {}) {
305
319
  return axiosCloudAPI.post("/track", {
@@ -314,18 +328,18 @@ async function save(data, { directoryPath } = {}) {
314
328
  const alreadyInFileData = await retrieve({ directoryPath });
315
329
  const storedData = { ...alreadyInFileData, ...data };
316
330
  const pathToFile = path__default.join(directoryPath || process.cwd(), LOCAL_SAVE_FILENAME);
317
- await fse.ensureDir(path__default.dirname(pathToFile));
318
- await fse.writeJson(pathToFile, storedData, { encoding: "utf8" });
331
+ await fse__default.ensureDir(path__default.dirname(pathToFile));
332
+ await fse__default.writeJson(pathToFile, storedData, { encoding: "utf8" });
319
333
  }
320
334
  async function retrieve({
321
335
  directoryPath
322
336
  } = {}) {
323
337
  const pathToFile = path__default.join(directoryPath || process.cwd(), LOCAL_SAVE_FILENAME);
324
- const pathExists = await fse.pathExists(pathToFile);
338
+ const pathExists = await fse__default.pathExists(pathToFile);
325
339
  if (!pathExists) {
326
340
  return {};
327
341
  }
328
- return fse.readJSON(pathToFile, { encoding: "utf8" });
342
+ return fse__default.readJSON(pathToFile, { encoding: "utf8" });
329
343
  }
330
344
  const strapiInfoSave = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
331
345
  __proto__: null,
@@ -570,6 +584,58 @@ const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePropert
570
584
  local: strapiInfoSave,
571
585
  tokenServiceFactory
572
586
  }, Symbol.toStringTag, { value: "Module" }));
587
+ yup.object({
588
+ name: yup.string().required(),
589
+ exports: yup.lazy(
590
+ (value) => yup.object(
591
+ typeof value === "object" ? Object.entries(value).reduce((acc, [key, value2]) => {
592
+ if (typeof value2 === "object") {
593
+ acc[key] = yup.object({
594
+ types: yup.string().optional(),
595
+ source: yup.string().required(),
596
+ module: yup.string().optional(),
597
+ import: yup.string().required(),
598
+ require: yup.string().required(),
599
+ default: yup.string().required()
600
+ }).noUnknown(true);
601
+ } else {
602
+ acc[key] = yup.string().matches(/^\.\/.*\.json$/).required();
603
+ }
604
+ return acc;
605
+ }, {}) : void 0
606
+ ).optional()
607
+ )
608
+ });
609
+ const loadPkg = async ({ cwd, logger }) => {
610
+ const pkgPath = await pkgUp({ cwd });
611
+ if (!pkgPath) {
612
+ throw new Error("Could not find a package.json in the current directory");
613
+ }
614
+ const buffer = await fse.readFile(pkgPath);
615
+ const pkg = JSON.parse(buffer.toString());
616
+ logger.debug("Loaded package.json:", os.EOL, pkg);
617
+ return pkg;
618
+ };
619
+ async function getProjectNameFromPackageJson(ctx) {
620
+ try {
621
+ const packageJson2 = await loadPkg(ctx);
622
+ return packageJson2.name || "my-strapi-project";
623
+ } catch (e) {
624
+ return "my-strapi-project";
625
+ }
626
+ }
627
+ function applyDefaultName(newDefaultName, questions, defaultValues) {
628
+ const newDefaultValues = _.cloneDeep(defaultValues);
629
+ newDefaultValues.name = newDefaultName;
630
+ const newQuestions = questions.map((question) => {
631
+ const questionCopy = _.cloneDeep(question);
632
+ if (questionCopy.name === "name") {
633
+ questionCopy.default = newDefaultName;
634
+ }
635
+ return questionCopy;
636
+ });
637
+ return { newQuestions, newDefaultValues };
638
+ }
573
639
  const openModule$1 = import("open");
574
640
  async function promptLogin(ctx) {
575
641
  const response = await inquirer.prompt([
@@ -785,7 +851,7 @@ async function createProject$1(ctx, cloudApi, projectInput) {
785
851
  throw e;
786
852
  }
787
853
  }
788
- const action$2 = async (ctx) => {
854
+ const action$3 = async (ctx) => {
789
855
  const { logger } = ctx;
790
856
  const { getValidToken, eraseToken } = await tokenServiceFactory(ctx);
791
857
  const token = await getValidToken(ctx, promptLogin);
@@ -794,7 +860,11 @@ const action$2 = async (ctx) => {
794
860
  }
795
861
  const cloudApi = await cloudApiFactory(ctx, token);
796
862
  const { data: config } = await cloudApi.config();
797
- const { questions, defaults: defaultValues } = config.projectCreation;
863
+ const { newQuestions: questions, newDefaultValues: defaultValues } = applyDefaultName(
864
+ await getProjectNameFromPackageJson(ctx),
865
+ config.projectCreation.questions,
866
+ config.projectCreation.defaults
867
+ );
798
868
  const projectAnswersDefaulted = defaults(defaultValues);
799
869
  const projectAnswers = await inquirer.prompt(questions);
800
870
  const projectInput = projectAnswersDefaulted(projectAnswers);
@@ -843,38 +913,6 @@ function notificationServiceFactory({ logger }) {
843
913
  };
844
914
  };
845
915
  }
846
- yup.object({
847
- name: yup.string().required(),
848
- exports: yup.lazy(
849
- (value) => yup.object(
850
- typeof value === "object" ? Object.entries(value).reduce((acc, [key, value2]) => {
851
- if (typeof value2 === "object") {
852
- acc[key] = yup.object({
853
- types: yup.string().optional(),
854
- source: yup.string().required(),
855
- module: yup.string().optional(),
856
- import: yup.string().required(),
857
- require: yup.string().required(),
858
- default: yup.string().required()
859
- }).noUnknown(true);
860
- } else {
861
- acc[key] = yup.string().matches(/^\.\/.*\.json$/).required();
862
- }
863
- return acc;
864
- }, {}) : void 0
865
- ).optional()
866
- )
867
- });
868
- const loadPkg = async ({ cwd, logger }) => {
869
- const pkgPath = await pkgUp({ cwd });
870
- if (!pkgPath) {
871
- throw new Error("Could not find a package.json in the current directory");
872
- }
873
- const buffer = await fs$1.readFile(pkgPath);
874
- const pkg = JSON.parse(buffer.toString());
875
- logger.debug("Loaded package.json:", os.EOL, pkg);
876
- return pkg;
877
- };
878
916
  const buildLogsServiceFactory = ({ logger }) => {
879
917
  return async (url, token, cliConfig2) => {
880
918
  const CONN_TIMEOUT = Number(cliConfig2.buildLogsConnectionTimeout);
@@ -970,13 +1008,13 @@ async function upload(ctx, project, token, maxProjectFileSize) {
970
1008
  process.exit(1);
971
1009
  }
972
1010
  const tarFilePath = path__default.resolve(storagePath, compressedFilename);
973
- const fileStats = await fse.stat(tarFilePath);
1011
+ const fileStats = await fse__default.stat(tarFilePath);
974
1012
  if (fileStats.size > maxProjectFileSize) {
975
1013
  ctx.logger.log(
976
1014
  "Unable to proceed: Your project is too big to be transferred, please use a git repo instead."
977
1015
  );
978
1016
  try {
979
- await fse.remove(tarFilePath);
1017
+ await fse__default.remove(tarFilePath);
980
1018
  } catch (e) {
981
1019
  ctx.logger.log("Unable to remove file: ", tarFilePath);
982
1020
  ctx.logger.debug(e);
@@ -1015,7 +1053,7 @@ async function upload(ctx, project, token, maxProjectFileSize) {
1015
1053
  }
1016
1054
  ctx.logger.debug(e);
1017
1055
  } finally {
1018
- await fse.remove(tarFilePath);
1056
+ await fse__default.remove(tarFilePath);
1019
1057
  }
1020
1058
  process.exit(0);
1021
1059
  } catch (e) {
@@ -1028,7 +1066,7 @@ async function getProject(ctx) {
1028
1066
  const { project } = await retrieve();
1029
1067
  if (!project) {
1030
1068
  try {
1031
- return await action$2(ctx);
1069
+ return await action$3(ctx);
1032
1070
  } catch (e) {
1033
1071
  ctx.logger.error("An error occurred while deploying the project. Please try again later.");
1034
1072
  ctx.logger.debug(e);
@@ -1037,7 +1075,7 @@ async function getProject(ctx) {
1037
1075
  }
1038
1076
  return project;
1039
1077
  }
1040
- const action$1 = async (ctx) => {
1078
+ const action$2 = async (ctx) => {
1041
1079
  const { getValidToken } = await tokenServiceFactory(ctx);
1042
1080
  const token = await getValidToken(ctx, promptLogin);
1043
1081
  if (!token) {
@@ -1111,16 +1149,16 @@ const runAction = (name2, action2) => (...args) => {
1111
1149
  process.exit(1);
1112
1150
  });
1113
1151
  };
1114
- const command$3 = ({ command: command2, ctx }) => {
1115
- command2.command("cloud:deploy").alias("deploy").description("Deploy a Strapi Cloud project").option("-d, --debug", "Enable debugging mode with verbose logs").option("-s, --silent", "Don't log anything").action(() => runAction("deploy", action$1)(ctx));
1152
+ const command$4 = ({ command: command2, ctx }) => {
1153
+ command2.command("cloud:deploy").alias("deploy").description("Deploy a Strapi Cloud project").option("-d, --debug", "Enable debugging mode with verbose logs").option("-s, --silent", "Don't log anything").action(() => runAction("deploy", action$2)(ctx));
1116
1154
  };
1117
1155
  const deployProject = {
1118
1156
  name: "deploy-project",
1119
1157
  description: "Deploy a Strapi Cloud project",
1120
- action: action$1,
1121
- command: command$3
1158
+ action: action$2,
1159
+ command: command$4
1122
1160
  };
1123
- const command$2 = ({ command: command2, ctx }) => {
1161
+ const command$3 = ({ command: command2, ctx }) => {
1124
1162
  command2.command("cloud:login").alias("login").description("Strapi Cloud Login").addHelpText(
1125
1163
  "after",
1126
1164
  "\nAfter running this command, you will be prompted to enter your authentication information."
@@ -1130,10 +1168,10 @@ const login = {
1130
1168
  name: "login",
1131
1169
  description: "Strapi Cloud Login",
1132
1170
  action: loginAction,
1133
- command: command$2
1171
+ command: command$3
1134
1172
  };
1135
1173
  const openModule = import("open");
1136
- const action = async (ctx) => {
1174
+ const action$1 = async (ctx) => {
1137
1175
  const { logger } = ctx;
1138
1176
  const { retrieveToken, eraseToken } = await tokenServiceFactory(ctx);
1139
1177
  const token = await retrieveToken();
@@ -1169,31 +1207,61 @@ const action = async (ctx) => {
1169
1207
  logger.debug("Failed to track logout event", e);
1170
1208
  }
1171
1209
  };
1172
- const command$1 = ({ command: command2, ctx }) => {
1173
- command2.command("cloud:logout").alias("logout").description("Strapi Cloud Logout").option("-d, --debug", "Enable debugging mode with verbose logs").option("-s, --silent", "Don't log anything").action(() => runAction("logout", action)(ctx));
1210
+ const command$2 = ({ command: command2, ctx }) => {
1211
+ command2.command("cloud:logout").alias("logout").description("Strapi Cloud Logout").option("-d, --debug", "Enable debugging mode with verbose logs").option("-s, --silent", "Don't log anything").action(() => runAction("logout", action$1)(ctx));
1174
1212
  };
1175
1213
  const logout = {
1176
1214
  name: "logout",
1177
1215
  description: "Strapi Cloud Logout",
1178
- action,
1179
- command: command$1
1216
+ action: action$1,
1217
+ command: command$2
1180
1218
  };
1181
- const command = ({ command: command2, ctx }) => {
1182
- command2.command("cloud:create-project").description("Create a Strapi Cloud project").option("-d, --debug", "Enable debugging mode with verbose logs").option("-s, --silent", "Don't log anything").action(() => runAction("cloud:create-project", action$2)(ctx));
1219
+ const command$1 = ({ command: command2, ctx }) => {
1220
+ command2.command("cloud:create-project").description("Create a Strapi Cloud project").option("-d, --debug", "Enable debugging mode with verbose logs").option("-s, --silent", "Don't log anything").action(() => runAction("cloud:create-project", action$3)(ctx));
1183
1221
  };
1184
1222
  const createProject = {
1185
1223
  name: "create-project",
1186
1224
  description: "Create a new project",
1187
- action: action$2,
1225
+ action: action$3,
1226
+ command: command$1
1227
+ };
1228
+ const action = async (ctx) => {
1229
+ const { getValidToken } = await tokenServiceFactory(ctx);
1230
+ const token = await getValidToken(ctx, promptLogin);
1231
+ const { logger } = ctx;
1232
+ if (!token) {
1233
+ return;
1234
+ }
1235
+ const cloudApiService = await cloudApiFactory(ctx, token);
1236
+ const spinner = logger.spinner("Fetching your projects...").start();
1237
+ try {
1238
+ const {
1239
+ data: { data: projectList }
1240
+ } = await cloudApiService.listProjects();
1241
+ spinner.succeed();
1242
+ logger.log(projectList);
1243
+ } catch (e) {
1244
+ ctx.logger.debug("Failed to list projects", e);
1245
+ spinner.fail("An error occurred while fetching your projects from Strapi Cloud.");
1246
+ }
1247
+ };
1248
+ const command = ({ command: command2, ctx }) => {
1249
+ command2.command("cloud:projects").alias("projects").description("List Strapi Cloud projects").option("-d, --debug", "Enable debugging mode with verbose logs").option("-s, --silent", "Don't log anything").action(() => runAction("listProjects", action)(ctx));
1250
+ };
1251
+ const listProjects = {
1252
+ name: "list-projects",
1253
+ description: "List Strapi Cloud projects",
1254
+ action,
1188
1255
  command
1189
1256
  };
1190
1257
  const cli = {
1191
1258
  deployProject,
1192
1259
  login,
1193
1260
  logout,
1194
- createProject
1261
+ createProject,
1262
+ listProjects
1195
1263
  };
1196
- const cloudCommands = [deployProject, login, logout];
1264
+ const cloudCommands = [deployProject, login, logout, listProjects];
1197
1265
  async function initCloudCLIConfig() {
1198
1266
  const localConfig = await getLocalConfig();
1199
1267
  if (!localConfig.deviceId) {