@strapi/strapi 5.12.0 → 5.12.2

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.
Files changed (263) hide show
  1. package/dist/cli.js +89 -45
  2. package/dist/cli.js.map +1 -1
  3. package/dist/cli.mjs +90 -42
  4. package/dist/cli.mjs.map +1 -1
  5. package/dist/package.json.js +6 -0
  6. package/dist/package.json.js.map +1 -0
  7. package/dist/package.json.mjs +4 -0
  8. package/dist/package.json.mjs.map +1 -0
  9. package/dist/src/cli/commands/admin/create-user.js +124 -0
  10. package/dist/src/cli/commands/admin/create-user.js.map +1 -0
  11. package/dist/src/cli/commands/admin/create-user.mjs +121 -0
  12. package/dist/src/cli/commands/admin/create-user.mjs.map +1 -0
  13. package/dist/src/cli/commands/admin/reset-user-password.js +61 -0
  14. package/dist/src/cli/commands/admin/reset-user-password.js.map +1 -0
  15. package/dist/src/cli/commands/admin/reset-user-password.mjs +58 -0
  16. package/dist/src/cli/commands/admin/reset-user-password.mjs.map +1 -0
  17. package/dist/src/cli/commands/build.js +29 -0
  18. package/dist/src/cli/commands/build.js.map +1 -0
  19. package/dist/src/cli/commands/build.mjs +27 -0
  20. package/dist/src/cli/commands/build.mjs.map +1 -0
  21. package/dist/src/cli/commands/components/list.js +32 -0
  22. package/dist/src/cli/commands/components/list.js.map +1 -0
  23. package/dist/src/cli/commands/components/list.mjs +29 -0
  24. package/dist/src/cli/commands/components/list.mjs.map +1 -0
  25. package/dist/src/cli/commands/configuration/dump.js +53 -0
  26. package/dist/src/cli/commands/configuration/dump.js.map +1 -0
  27. package/dist/src/cli/commands/configuration/dump.mjs +50 -0
  28. package/dist/src/cli/commands/configuration/dump.mjs.map +1 -0
  29. package/dist/src/cli/commands/configuration/restore.js +173 -0
  30. package/dist/src/cli/commands/configuration/restore.js.map +1 -0
  31. package/dist/src/cli/commands/configuration/restore.mjs +170 -0
  32. package/dist/src/cli/commands/configuration/restore.mjs.map +1 -0
  33. package/dist/src/cli/commands/console.js +31 -0
  34. package/dist/src/cli/commands/console.js.map +1 -0
  35. package/dist/src/cli/commands/console.mjs +28 -0
  36. package/dist/src/cli/commands/console.mjs.map +1 -0
  37. package/dist/src/cli/commands/content-types/list.js +32 -0
  38. package/dist/src/cli/commands/content-types/list.js.map +1 -0
  39. package/dist/src/cli/commands/content-types/list.mjs +29 -0
  40. package/dist/src/cli/commands/content-types/list.mjs.map +1 -0
  41. package/dist/src/cli/commands/controllers/list.js +32 -0
  42. package/dist/src/cli/commands/controllers/list.js.map +1 -0
  43. package/dist/src/cli/commands/controllers/list.mjs +29 -0
  44. package/dist/src/cli/commands/controllers/list.mjs.map +1 -0
  45. package/dist/src/cli/commands/develop.js +32 -0
  46. package/dist/src/cli/commands/develop.js.map +1 -0
  47. package/dist/src/cli/commands/develop.mjs +30 -0
  48. package/dist/src/cli/commands/develop.mjs.map +1 -0
  49. package/dist/src/cli/commands/export/action.js +126 -0
  50. package/dist/src/cli/commands/export/action.js.map +1 -0
  51. package/dist/src/cli/commands/export/action.mjs +124 -0
  52. package/dist/src/cli/commands/export/action.mjs.map +1 -0
  53. package/dist/src/cli/commands/export/command.js +15 -0
  54. package/dist/src/cli/commands/export/command.js.map +1 -0
  55. package/dist/src/cli/commands/export/command.mjs +13 -0
  56. package/dist/src/cli/commands/export/command.mjs.map +1 -0
  57. package/dist/src/cli/commands/generate.js +18 -0
  58. package/dist/src/cli/commands/generate.js.map +1 -0
  59. package/dist/src/cli/commands/generate.mjs +16 -0
  60. package/dist/src/cli/commands/generate.mjs.map +1 -0
  61. package/dist/src/cli/commands/hooks/list.js +32 -0
  62. package/dist/src/cli/commands/hooks/list.js.map +1 -0
  63. package/dist/src/cli/commands/hooks/list.mjs +29 -0
  64. package/dist/src/cli/commands/hooks/list.mjs.map +1 -0
  65. package/dist/src/cli/commands/import/action.js +124 -0
  66. package/dist/src/cli/commands/import/action.js.map +1 -0
  67. package/dist/src/cli/commands/import/action.mjs +122 -0
  68. package/dist/src/cli/commands/import/action.mjs.map +1 -0
  69. package/dist/src/cli/commands/import/command.js +59 -0
  70. package/dist/src/cli/commands/import/command.js.map +1 -0
  71. package/dist/src/cli/commands/import/command.mjs +57 -0
  72. package/dist/src/cli/commands/import/command.mjs.map +1 -0
  73. package/dist/src/cli/commands/index.js +64 -0
  74. package/dist/src/cli/commands/index.js.map +1 -0
  75. package/dist/src/cli/commands/index.mjs +62 -0
  76. package/dist/src/cli/commands/index.mjs.map +1 -0
  77. package/dist/src/cli/commands/middlewares/list.js +32 -0
  78. package/dist/src/cli/commands/middlewares/list.js.map +1 -0
  79. package/dist/src/cli/commands/middlewares/list.mjs +29 -0
  80. package/dist/src/cli/commands/middlewares/list.mjs.map +1 -0
  81. package/dist/src/cli/commands/policies/list.js +32 -0
  82. package/dist/src/cli/commands/policies/list.js.map +1 -0
  83. package/dist/src/cli/commands/policies/list.mjs +29 -0
  84. package/dist/src/cli/commands/policies/list.mjs.map +1 -0
  85. package/dist/src/cli/commands/report.js +39 -0
  86. package/dist/src/cli/commands/report.js.map +1 -0
  87. package/dist/src/cli/commands/report.mjs +37 -0
  88. package/dist/src/cli/commands/report.mjs.map +1 -0
  89. package/dist/src/cli/commands/routes/list.js +40 -0
  90. package/dist/src/cli/commands/routes/list.js.map +1 -0
  91. package/dist/src/cli/commands/routes/list.mjs +37 -0
  92. package/dist/src/cli/commands/routes/list.mjs.map +1 -0
  93. package/dist/src/cli/commands/services/list.js +32 -0
  94. package/dist/src/cli/commands/services/list.js.map +1 -0
  95. package/dist/src/cli/commands/services/list.mjs +29 -0
  96. package/dist/src/cli/commands/services/list.mjs.map +1 -0
  97. package/dist/src/cli/commands/start.js +28 -0
  98. package/dist/src/cli/commands/start.js.map +1 -0
  99. package/dist/src/cli/commands/start.mjs +26 -0
  100. package/dist/src/cli/commands/start.mjs.map +1 -0
  101. package/dist/src/cli/commands/telemetry/disable.js +72 -0
  102. package/dist/src/cli/commands/telemetry/disable.js.map +1 -0
  103. package/dist/src/cli/commands/telemetry/disable.mjs +69 -0
  104. package/dist/src/cli/commands/telemetry/disable.mjs.map +1 -0
  105. package/dist/src/cli/commands/telemetry/enable.js +88 -0
  106. package/dist/src/cli/commands/telemetry/enable.js.map +1 -0
  107. package/dist/src/cli/commands/telemetry/enable.mjs +85 -0
  108. package/dist/src/cli/commands/telemetry/enable.mjs.map +1 -0
  109. package/dist/src/cli/commands/templates/generate.js +15 -0
  110. package/dist/src/cli/commands/templates/generate.js.map +1 -0
  111. package/dist/src/cli/commands/templates/generate.mjs +13 -0
  112. package/dist/src/cli/commands/templates/generate.mjs.map +1 -0
  113. package/dist/src/cli/commands/transfer/action.js +141 -0
  114. package/dist/src/cli/commands/transfer/action.js.map +1 -0
  115. package/dist/src/cli/commands/transfer/action.mjs +139 -0
  116. package/dist/src/cli/commands/transfer/action.mjs.map +1 -0
  117. package/dist/src/cli/commands/transfer/command.js +61 -0
  118. package/dist/src/cli/commands/transfer/command.js.map +1 -0
  119. package/dist/src/cli/commands/transfer/command.mjs +59 -0
  120. package/dist/src/cli/commands/transfer/command.mjs.map +1 -0
  121. package/dist/src/cli/commands/ts/generate-types.js +40 -0
  122. package/dist/src/cli/commands/ts/generate-types.js.map +1 -0
  123. package/dist/src/cli/commands/ts/generate-types.mjs +37 -0
  124. package/dist/src/cli/commands/ts/generate-types.mjs.map +1 -0
  125. package/dist/src/cli/commands/version.js +17 -0
  126. package/dist/src/cli/commands/version.js.map +1 -0
  127. package/dist/src/cli/commands/version.mjs +15 -0
  128. package/dist/src/cli/commands/version.mjs.map +1 -0
  129. package/dist/src/cli/utils/commander.js +125 -0
  130. package/dist/src/cli/utils/commander.js.map +1 -0
  131. package/dist/src/cli/utils/commander.mjs +116 -0
  132. package/dist/src/cli/utils/commander.mjs.map +1 -0
  133. package/dist/src/cli/utils/data-transfer.js +378 -0
  134. package/dist/src/cli/utils/data-transfer.js.map +1 -0
  135. package/dist/src/cli/utils/data-transfer.mjs +359 -0
  136. package/dist/src/cli/utils/data-transfer.mjs.map +1 -0
  137. package/dist/src/cli/utils/helpers.js +114 -0
  138. package/dist/src/cli/utils/helpers.js.map +1 -0
  139. package/dist/src/cli/utils/helpers.mjs +107 -0
  140. package/dist/src/cli/utils/helpers.mjs.map +1 -0
  141. package/dist/src/cli/utils/logger.js +125 -0
  142. package/dist/src/cli/utils/logger.js.map +1 -0
  143. package/dist/src/cli/utils/logger.mjs +104 -0
  144. package/dist/src/cli/utils/logger.mjs.map +1 -0
  145. package/dist/src/cli/utils/telemetry.js +27 -0
  146. package/dist/src/cli/utils/telemetry.js.map +1 -0
  147. package/dist/src/cli/utils/telemetry.mjs +25 -0
  148. package/dist/src/cli/utils/telemetry.mjs.map +1 -0
  149. package/dist/src/cli/utils/tsconfig.js +25 -0
  150. package/dist/src/cli/utils/tsconfig.js.map +1 -0
  151. package/dist/src/cli/utils/tsconfig.mjs +23 -0
  152. package/dist/src/cli/utils/tsconfig.mjs.map +1 -0
  153. package/dist/src/node/build.js +90 -0
  154. package/dist/src/node/build.js.map +1 -0
  155. package/dist/src/node/build.mjs +69 -0
  156. package/dist/src/node/build.mjs.map +1 -0
  157. package/dist/src/node/core/admin-customisations.js +27 -0
  158. package/dist/src/node/core/admin-customisations.js.map +1 -0
  159. package/dist/src/node/core/admin-customisations.mjs +25 -0
  160. package/dist/src/node/core/admin-customisations.mjs.map +1 -0
  161. package/dist/{chunks/aliases-B2TXon3T.js → src/node/core/aliases.js} +3 -42
  162. package/dist/src/node/core/aliases.js.map +1 -0
  163. package/dist/{chunks/aliases-BsT_zzr4.mjs → src/node/core/aliases.mjs} +4 -41
  164. package/dist/src/node/core/aliases.mjs.map +1 -0
  165. package/dist/src/node/core/config.js +18 -0
  166. package/dist/src/node/core/config.js.map +1 -0
  167. package/dist/src/node/core/config.mjs +16 -0
  168. package/dist/src/node/core/config.mjs.map +1 -0
  169. package/dist/src/node/core/dependencies.js +186 -0
  170. package/dist/src/node/core/dependencies.js.map +1 -0
  171. package/dist/src/node/core/dependencies.mjs +183 -0
  172. package/dist/src/node/core/dependencies.mjs.map +1 -0
  173. package/dist/src/node/core/env.js +32 -0
  174. package/dist/src/node/core/env.js.map +1 -0
  175. package/dist/src/node/core/env.mjs +29 -0
  176. package/dist/src/node/core/env.mjs.map +1 -0
  177. package/dist/src/node/core/errors.js +51 -0
  178. package/dist/src/node/core/errors.js.map +1 -0
  179. package/dist/src/node/core/errors.mjs +48 -0
  180. package/dist/src/node/core/errors.mjs.map +1 -0
  181. package/dist/src/node/core/files.js +68 -0
  182. package/dist/src/node/core/files.js.map +1 -0
  183. package/dist/src/node/core/files.mjs +63 -0
  184. package/dist/src/node/core/files.mjs.map +1 -0
  185. package/dist/src/node/core/managers.js +27 -0
  186. package/dist/src/node/core/managers.js.map +1 -0
  187. package/dist/src/node/core/managers.mjs +25 -0
  188. package/dist/src/node/core/managers.mjs.map +1 -0
  189. package/dist/src/node/core/monorepo.js +30 -0
  190. package/dist/src/node/core/monorepo.js.map +1 -0
  191. package/dist/src/node/core/monorepo.mjs +28 -0
  192. package/dist/src/node/core/monorepo.mjs.map +1 -0
  193. package/dist/src/node/core/plugins.js +140 -0
  194. package/dist/src/node/core/plugins.js.map +1 -0
  195. package/dist/src/node/core/plugins.mjs +137 -0
  196. package/dist/src/node/core/plugins.mjs.map +1 -0
  197. package/dist/src/node/core/timer.js +33 -0
  198. package/dist/src/node/core/timer.js.map +1 -0
  199. package/dist/src/node/core/timer.mjs +30 -0
  200. package/dist/src/node/core/timer.mjs.map +1 -0
  201. package/dist/src/node/create-build-context.js +111 -0
  202. package/dist/src/node/create-build-context.js.map +1 -0
  203. package/dist/src/node/create-build-context.mjs +109 -0
  204. package/dist/src/node/create-build-context.mjs.map +1 -0
  205. package/dist/src/node/develop.js +312 -0
  206. package/dist/src/node/develop.js.map +1 -0
  207. package/dist/src/node/develop.mjs +291 -0
  208. package/dist/src/node/develop.mjs.map +1 -0
  209. package/dist/src/node/staticFiles.js +81 -0
  210. package/dist/src/node/staticFiles.js.map +1 -0
  211. package/dist/src/node/staticFiles.mjs +78 -0
  212. package/dist/src/node/staticFiles.mjs.map +1 -0
  213. package/dist/src/node/vite/build.js +14 -0
  214. package/dist/src/node/vite/build.js.map +1 -0
  215. package/dist/src/node/vite/build.mjs +12 -0
  216. package/dist/src/node/vite/build.mjs.map +1 -0
  217. package/dist/{chunks/config-CsJ2EtSs.js → src/node/vite/config.js} +12 -50
  218. package/dist/src/node/vite/config.js.map +1 -0
  219. package/dist/{chunks/config-BqQdQJh_.mjs → src/node/vite/config.mjs} +6 -44
  220. package/dist/src/node/vite/config.mjs.map +1 -0
  221. package/dist/src/node/vite/plugins.js +46 -0
  222. package/dist/src/node/vite/plugins.js.map +1 -0
  223. package/dist/src/node/vite/plugins.mjs +44 -0
  224. package/dist/src/node/vite/plugins.mjs.map +1 -0
  225. package/dist/{chunks/watch-DzwyHBm4.js → src/node/vite/watch.js} +2 -45
  226. package/dist/src/node/vite/watch.js.map +1 -0
  227. package/dist/{chunks/watch-BDsNoG4h.mjs → src/node/vite/watch.mjs} +2 -45
  228. package/dist/src/node/vite/watch.mjs.map +1 -0
  229. package/dist/src/node/webpack/build.js +37 -0
  230. package/dist/src/node/webpack/build.js.map +1 -0
  231. package/dist/src/node/webpack/build.mjs +35 -0
  232. package/dist/src/node/webpack/build.mjs.map +1 -0
  233. package/dist/{chunks/config-B2BETQ-W.js → src/node/webpack/config.js} +10 -8
  234. package/dist/src/node/webpack/config.js.map +1 -0
  235. package/dist/{chunks/config-DHibCzCW.mjs → src/node/webpack/config.mjs} +5 -3
  236. package/dist/src/node/webpack/config.mjs.map +1 -0
  237. package/dist/{chunks/watch-BKW3b7Rj.js → src/node/webpack/watch.js} +2 -51
  238. package/dist/src/node/webpack/watch.js.map +1 -0
  239. package/dist/{chunks/watch-DQJ0HV0l.mjs → src/node/webpack/watch.mjs} +2 -51
  240. package/dist/src/node/webpack/watch.mjs.map +1 -0
  241. package/package.json +22 -22
  242. package/dist/chunks/aliases-B2TXon3T.js.map +0 -1
  243. package/dist/chunks/aliases-BsT_zzr4.mjs.map +0 -1
  244. package/dist/chunks/build-BQMdiYL1.mjs +0 -57
  245. package/dist/chunks/build-BQMdiYL1.mjs.map +0 -1
  246. package/dist/chunks/build-FJLfahVt.mjs +0 -85
  247. package/dist/chunks/build-FJLfahVt.mjs.map +0 -1
  248. package/dist/chunks/build-RZ8_oqFy.js +0 -59
  249. package/dist/chunks/build-RZ8_oqFy.js.map +0 -1
  250. package/dist/chunks/build-XY97xrwm.js +0 -87
  251. package/dist/chunks/build-XY97xrwm.js.map +0 -1
  252. package/dist/chunks/config-B2BETQ-W.js.map +0 -1
  253. package/dist/chunks/config-BqQdQJh_.mjs.map +0 -1
  254. package/dist/chunks/config-CsJ2EtSs.js.map +0 -1
  255. package/dist/chunks/config-DHibCzCW.mjs.map +0 -1
  256. package/dist/chunks/index-BJFfd72y.js +0 -3185
  257. package/dist/chunks/index-BJFfd72y.js.map +0 -1
  258. package/dist/chunks/index-DL_emYoN.mjs +0 -3160
  259. package/dist/chunks/index-DL_emYoN.mjs.map +0 -1
  260. package/dist/chunks/watch-BDsNoG4h.mjs.map +0 -1
  261. package/dist/chunks/watch-BKW3b7Rj.js.map +0 -1
  262. package/dist/chunks/watch-DQJ0HV0l.mjs.map +0 -1
  263. package/dist/chunks/watch-DzwyHBm4.js.map +0 -1
@@ -1,3185 +0,0 @@
1
- 'use strict';
2
-
3
- var commander = require('commander');
4
- var cloudCli = require('@strapi/cloud-cli');
5
- var utils = require('@strapi/utils');
6
- var _ = require('lodash');
7
- var inquirer = require('inquirer');
8
- var core = require('@strapi/core');
9
- var chalk = require('chalk');
10
- var fp = require('lodash/fp');
11
- var boxen = require('boxen');
12
- var CLITable = require('cli-table3');
13
- var fs = require('fs');
14
- var path = require('path');
15
- var fse = require('fs-extra');
16
- var crypto = require('crypto');
17
- var tsUtils = require('@strapi/typescript-utils');
18
- var os = require('node:os');
19
- var fs$1 = require('node:fs/promises');
20
- var path$1 = require('node:path');
21
- var semver = require('semver');
22
- var resolveFrom = require('resolve-from');
23
- var execa = require('execa');
24
- var readPkgUp = require('read-pkg-up');
25
- var perf_hooks = require('perf_hooks');
26
- var browserslist = require('browserslist');
27
- var dotenv = require('dotenv');
28
- var node = require('esbuild-register/dist/node');
29
- var fs$2 = require('node:fs');
30
- var camelCase = require('lodash/camelCase');
31
- var outdent = require('outdent');
32
- var react = require('react');
33
- var server = require('react-dom/server');
34
- var _internal = require('@strapi/admin/_internal');
35
- var REPL = require('repl');
36
- var cluster = require('node:cluster');
37
- var chokidar = require('chokidar');
38
- var os$1 = require('os');
39
- var logger = require('@strapi/logger');
40
- var ora = require('ora');
41
- var dataTransfer = require('@strapi/data-transfer');
42
- var cliProgress = require('cli-progress');
43
- var ts = require('typescript');
44
-
45
- function _interopNamespaceDefault(e) {
46
- var n = Object.create(null);
47
- if (e) {
48
- Object.keys(e).forEach(function (k) {
49
- if (k !== 'default') {
50
- var d = Object.getOwnPropertyDescriptor(e, k);
51
- Object.defineProperty(n, k, d.get ? d : {
52
- enumerable: true,
53
- get: function () { return e[k]; }
54
- });
55
- }
56
- });
57
- }
58
- n.default = e;
59
- return Object.freeze(n);
60
- }
61
-
62
- var tsUtils__namespace = /*#__PURE__*/_interopNamespaceDefault(tsUtils);
63
- var cliProgress__namespace = /*#__PURE__*/_interopNamespaceDefault(cliProgress);
64
-
65
- /**
66
- * Helper functions for the Strapi CLI
67
- */ const bytesPerKb = 1024;
68
- const sizes = [
69
- 'B ',
70
- 'KB',
71
- 'MB',
72
- 'GB',
73
- 'TB',
74
- 'PB'
75
- ];
76
- /**
77
- * Convert bytes to a human readable formatted string, for example "1024" becomes "1KB"
78
- */ const readableBytes = (bytes, decimals = 1, padStart = 0)=>{
79
- if (!bytes) {
80
- return '0';
81
- }
82
- const i = Math.floor(Math.log(bytes) / Math.log(bytesPerKb));
83
- const result = `${parseFloat((bytes / bytesPerKb ** i).toFixed(decimals))} ${sizes[i].padStart(2)}`;
84
- return result.padStart(padStart);
85
- };
86
- /**
87
- *
88
- * Display message(s) to console and then call process.exit with code.
89
- * If code is zero, console.log and green text is used for messages, otherwise console.error and red text.
90
- *
91
- */ const exitWith = (code, message, options = {})=>{
92
- const { logger = console, prc = process } = options;
93
- const log = (message)=>{
94
- if (code === 0) {
95
- logger.log(chalk.green(message));
96
- } else {
97
- logger.error(chalk.red(message));
98
- }
99
- };
100
- if (fp.isString(message)) {
101
- log(message);
102
- } else if (fp.isArray(message)) {
103
- message.forEach((msg)=>log(msg));
104
- }
105
- prc.exit(code);
106
- };
107
- /**
108
- * assert that a URL object has a protocol value
109
- *
110
- */ const assertUrlHasProtocol = (url, protocol)=>{
111
- if (!url.protocol) {
112
- exitWith(1, `${url.toString()} does not have a protocol`);
113
- }
114
- // if just checking for the existence of a protocol, return
115
- if (!protocol) {
116
- return;
117
- }
118
- if (fp.isString(protocol)) {
119
- if (protocol !== url.protocol) {
120
- exitWith(1, `${url.toString()} must have the protocol ${protocol}`);
121
- }
122
- return;
123
- }
124
- // assume an array
125
- if (!protocol.some((protocol)=>url.protocol === protocol)) {
126
- return exitWith(1, `${url.toString()} must have one of the following protocols: ${protocol.join(',')}`);
127
- }
128
- };
129
- /**
130
- * Passes commander options to conditionCallback(). If it returns true, call isMetCallback otherwise call isNotMetCallback
131
- */ const ifOptions = (conditionCallback, isMetCallback = async ()=>{}, isNotMetCallback = async ()=>{})=>{
132
- return async (command)=>{
133
- const opts = command.opts();
134
- if (await conditionCallback(opts)) {
135
- await isMetCallback(command);
136
- } else {
137
- await isNotMetCallback(command);
138
- }
139
- };
140
- };
141
- const assertCwdContainsStrapiProject = (name)=>{
142
- const logErrorAndExit = ()=>{
143
- console.log(`You need to run ${chalk.yellow(`strapi ${name}`)} in a Strapi project. Make sure you are in the right directory.`);
144
- process.exit(1);
145
- };
146
- try {
147
- const pkgJSON = require(`${process.cwd()}/package.json`);
148
- if (!fp.has('dependencies.@strapi/strapi', pkgJSON) && !fp.has('devDependencies.@strapi/strapi', pkgJSON)) {
149
- logErrorAndExit();
150
- }
151
- } catch (err) {
152
- logErrorAndExit();
153
- }
154
- };
155
- const runAction = (name, action)=>(...args)=>{
156
- assertCwdContainsStrapiProject(name);
157
- Promise.resolve().then(()=>{
158
- return action(...args);
159
- }).catch((error)=>{
160
- console.error(error);
161
- process.exit(1);
162
- });
163
- };
164
-
165
- const emailValidator = utils.yup.string().email('Invalid email address').lowercase();
166
- const passwordValidator = utils.yup.string().min(8, 'Password must be at least 8 characters long').matches(/[a-z]/, 'Password must contain at least one lowercase character').matches(/[A-Z]/, 'Password must contain at least one uppercase character').matches(/\d/, 'Password must contain at least one number');
167
- const adminCreateSchema = utils.yup.object().shape({
168
- email: emailValidator,
169
- password: passwordValidator,
170
- firstname: utils.yup.string().trim().required('First name is required'),
171
- lastname: utils.yup.string()
172
- });
173
- /**
174
- * It's not an observable, in reality this is
175
- * `ReadOnlyArray<inquirer.DistinctQuestion<Answers>>`
176
- * but then the logic of the validate function needs to change.
177
- */ // eslint-disable-next-line rxjs/finnish
178
- const promptQuestions$1 = [
179
- {
180
- type: 'input',
181
- name: 'email',
182
- message: 'Admin email?',
183
- async validate (value) {
184
- const validEmail = await emailValidator.validate(value);
185
- return validEmail === value || validEmail;
186
- }
187
- },
188
- {
189
- type: 'password',
190
- name: 'password',
191
- message: 'Admin password?',
192
- async validate (value) {
193
- const validPassword = await passwordValidator.validate(value);
194
- return validPassword === value || validPassword;
195
- }
196
- },
197
- {
198
- type: 'input',
199
- name: 'firstname',
200
- message: 'First name?'
201
- },
202
- {
203
- type: 'input',
204
- name: 'lastname',
205
- message: 'Last name?'
206
- },
207
- {
208
- type: 'confirm',
209
- name: 'confirm',
210
- message: 'Do you really want to create a new admin?'
211
- }
212
- ];
213
- async function createAdmin({ email, password, firstname, lastname }) {
214
- const appContext = await core.compileStrapi();
215
- const app = await core.createStrapi(appContext).load();
216
- const user = await app.admin.services.user.exists({
217
- email
218
- });
219
- if (user) {
220
- console.error(`User with email "${email}" already exists`);
221
- process.exit(1);
222
- }
223
- const superAdminRole = await app.admin.services.role.getSuperAdmin();
224
- await app.admin.services.user.create({
225
- email,
226
- firstname,
227
- lastname,
228
- isActive: true,
229
- roles: [
230
- superAdminRole.id
231
- ],
232
- ...password && {
233
- password,
234
- registrationToken: null
235
- }
236
- });
237
- console.log(`Successfully created new admin`);
238
- process.exit(0);
239
- }
240
- /**
241
- * Create new admin user
242
- */ const action$m = async (cmdOptions = {})=>{
243
- let { email, password, firstname, lastname } = cmdOptions;
244
- if (_.isEmpty(email) && _.isEmpty(password) && _.isEmpty(firstname) && _.isEmpty(lastname) && process.stdin.isTTY) {
245
- const inquiry = await inquirer.prompt(promptQuestions$1);
246
- if (!inquiry.confirm) {
247
- process.exit(0);
248
- }
249
- ({ email, password, firstname, lastname } = inquiry);
250
- }
251
- try {
252
- await adminCreateSchema.validate({
253
- email,
254
- password,
255
- firstname,
256
- lastname
257
- });
258
- } catch (err) {
259
- if (err instanceof utils.yup.ValidationError) {
260
- console.error(err.errors[0]);
261
- }
262
- process.exit(1);
263
- }
264
- return createAdmin({
265
- email,
266
- password,
267
- firstname,
268
- lastname
269
- });
270
- };
271
- /**
272
- * `$ strapi admin:create-user`
273
- */ const command$p = ()=>{
274
- return commander.createCommand('admin:create-user').alias('admin:create').description('Create a new admin').option('-e, --email <email>', 'Email of the new admin').option('-p, --password <password>', 'Password of the new admin').option('-f, --firstname <first name>', 'First name of the new admin').option('-l, --lastname <last name>', 'Last name of the new admin').action(runAction('admin:create-user', action$m));
275
- };
276
-
277
- const promptQuestions = [
278
- {
279
- type: 'input',
280
- name: 'email',
281
- message: 'User email?'
282
- },
283
- {
284
- type: 'password',
285
- name: 'password',
286
- message: 'New password?'
287
- },
288
- {
289
- type: 'confirm',
290
- name: 'confirm',
291
- message: "Do you really want to reset this user's password?"
292
- }
293
- ];
294
- async function changePassword({ email, password }) {
295
- const appContext = await core.compileStrapi();
296
- const app = await core.createStrapi(appContext).load();
297
- await app.admin.services.user.resetPasswordByEmail(email, password);
298
- console.log(`Successfully reset user's password`);
299
- process.exit(0);
300
- }
301
- /**
302
- * Reset user's password
303
- */ const action$l = async (cmdOptions = {})=>{
304
- const { email, password } = cmdOptions;
305
- if (_.isEmpty(email) && _.isEmpty(password) && process.stdin.isTTY) {
306
- const inquiry = await inquirer.prompt(promptQuestions);
307
- if (!inquiry.confirm) {
308
- process.exit(0);
309
- }
310
- return changePassword(inquiry);
311
- }
312
- if (_.isEmpty(email) || _.isEmpty(password)) {
313
- console.error('Missing required options `email` or `password`');
314
- process.exit(1);
315
- }
316
- return changePassword({
317
- email,
318
- password
319
- });
320
- };
321
- /**
322
- * `$ strapi admin:reset-user-password`
323
- */ const command$o = ()=>{
324
- return commander.createCommand('admin:reset-user-password').alias('admin:reset-password').description("Reset an admin user's password").option('-e, --email <email>', 'The user email').option('-p, --password <password>', 'New password for the user').action(runAction('admin:reset-user-password', action$l));
325
- };
326
-
327
- const action$k = async ()=>{
328
- const appContext = await core.compileStrapi();
329
- const app = await core.createStrapi(appContext).register();
330
- const list = Object.keys(app.components);
331
- const infoTable = new CLITable({
332
- head: [
333
- chalk.blue('Name')
334
- ]
335
- });
336
- list.forEach((name)=>infoTable.push([
337
- name
338
- ]));
339
- console.log(infoTable.toString());
340
- await app.destroy();
341
- };
342
- /**
343
- * `$ strapi components:list`
344
- */ const command$n = ()=>{
345
- return commander.createCommand('components:list').description('List all the application components').action(runAction('components:list', action$k));
346
- };
347
-
348
- const CHUNK_SIZE = 100;
349
- /**
350
- * Will dump configurations to a file or stdout
351
- * @param {string} file filepath to use as output
352
- */ const action$j = async ({ file: filePath, pretty })=>{
353
- const output = filePath ? fs.createWriteStream(filePath) : process.stdout;
354
- const appContext = await core.compileStrapi();
355
- const app = await core.createStrapi(appContext).load();
356
- const count = await app.query('strapi::core-store').count();
357
- const exportData = [];
358
- const pageCount = Math.ceil(count / CHUNK_SIZE);
359
- for(let page = 0; page < pageCount; page += 1){
360
- const results = await app.query('strapi::core-store').findMany({
361
- limit: CHUNK_SIZE,
362
- offset: page * CHUNK_SIZE,
363
- orderBy: 'key'
364
- });
365
- results.filter((result)=>result.key.startsWith('plugin_')).forEach((result)=>{
366
- exportData.push({
367
- key: result.key,
368
- value: result.value,
369
- type: result.type,
370
- environment: result.environment,
371
- tag: result.tag
372
- });
373
- });
374
- }
375
- const str = JSON.stringify(exportData, null, pretty ? 2 : undefined);
376
- output.write(str);
377
- output.write('\n');
378
- output.end();
379
- // log success only when writting to file
380
- if (filePath) {
381
- console.log(`Successfully exported ${exportData.length} configuration entries`);
382
- }
383
- process.exit(0);
384
- };
385
- /**
386
- * `$ strapi configuration:dump`
387
- */ const command$m = ()=>{
388
- return commander.createCommand('configuration:dump').alias('config:dump').description('Dump configurations of your application').option('-f, --file <file>', 'Output file, default output is stdout').option('-p, --pretty', 'Format the output JSON with indentation and line breaks', false).action(runAction('configuration:dump', action$j));
389
- };
390
-
391
- /**
392
- * Will restore configurations. It reads from a file or stdin
393
- */ const action$i = async ({ file: filePath, strategy = 'replace' })=>{
394
- const input = filePath ? fs.readFileSync(filePath) : await readStdin();
395
- const appContext = await core.compileStrapi();
396
- const app = await core.createStrapi(appContext).load();
397
- let dataToImport;
398
- try {
399
- dataToImport = JSON.parse(_.toString(input));
400
- } catch (error) {
401
- if (error instanceof Error) {
402
- throw new Error(`Invalid input data: ${error.message}. Expected a valid JSON array.`);
403
- }
404
- throw error;
405
- }
406
- if (!Array.isArray(dataToImport)) {
407
- throw new Error(`Invalid input data. Expected a valid JSON array.`);
408
- }
409
- if (!app.db) {
410
- throw new Error('Cannot import configuration without a database connection.');
411
- }
412
- const importer = createImporter(app.db, strategy);
413
- for (const config of dataToImport){
414
- await importer.import(config);
415
- }
416
- console.log(`Successfully imported configuration with ${strategy} strategy. Statistics: ${importer.printStatistics()}.`);
417
- process.exit(0);
418
- };
419
- const readStdin = ()=>{
420
- const { stdin } = process;
421
- let result = '';
422
- if (stdin.isTTY) return Promise.resolve(result);
423
- return new Promise((resolve, reject)=>{
424
- stdin.setEncoding('utf8');
425
- stdin.on('readable', ()=>{
426
- let chunk;
427
- // eslint-disable-next-line no-cond-assign
428
- while(chunk = stdin.read()){
429
- result += chunk;
430
- }
431
- });
432
- stdin.on('end', ()=>{
433
- resolve(result);
434
- });
435
- stdin.on('error', reject);
436
- });
437
- };
438
- const createImporter = (db, strategy)=>{
439
- switch(strategy){
440
- case 'replace':
441
- return createReplaceImporter(db);
442
- case 'merge':
443
- return createMergeImporter(db);
444
- case 'keep':
445
- return createKeepImporter(db);
446
- default:
447
- throw new Error(`No importer available for strategy "${strategy}"`);
448
- }
449
- };
450
- /**
451
- * Replace importer. Will replace the keys that already exist and create the new ones
452
- */ const createReplaceImporter = (db)=>{
453
- const stats = {
454
- created: 0,
455
- replaced: 0
456
- };
457
- return {
458
- printStatistics () {
459
- return `${stats.created} created, ${stats.replaced} replaced`;
460
- },
461
- async import (conf) {
462
- const matching = await db.query('strapi::core-store').count({
463
- where: {
464
- key: conf.key
465
- }
466
- });
467
- if (matching > 0) {
468
- stats.replaced += 1;
469
- await db.query('strapi::core-store').update({
470
- where: {
471
- key: conf.key
472
- },
473
- data: conf
474
- });
475
- } else {
476
- stats.created += 1;
477
- await db.query('strapi::core-store').create({
478
- data: conf
479
- });
480
- }
481
- }
482
- };
483
- };
484
- /**
485
- * Merge importer. Will merge the keys that already exist with their new value and create the new ones
486
- */ const createMergeImporter = (db)=>{
487
- const stats = {
488
- created: 0,
489
- merged: 0
490
- };
491
- return {
492
- printStatistics () {
493
- return `${stats.created} created, ${stats.merged} merged`;
494
- },
495
- async import (conf) {
496
- const existingConf = await db.query('strapi::core-store').findOne({
497
- where: {
498
- key: conf.key
499
- }
500
- });
501
- if (existingConf) {
502
- stats.merged += 1;
503
- await db.query('strapi::core-store').update({
504
- where: {
505
- key: conf.key
506
- },
507
- data: _.merge(existingConf, conf)
508
- });
509
- } else {
510
- stats.created += 1;
511
- await db.query('strapi::core-store').create({
512
- data: conf
513
- });
514
- }
515
- }
516
- };
517
- };
518
- /**
519
- * Merge importer. Will keep the keys that already exist without changing them and create the new ones
520
- */ const createKeepImporter = (db)=>{
521
- const stats = {
522
- created: 0,
523
- untouched: 0
524
- };
525
- return {
526
- printStatistics () {
527
- return `${stats.created} created, ${stats.untouched} untouched`;
528
- },
529
- async import (conf) {
530
- const matching = await db.query('strapi::core-store').count({
531
- where: {
532
- key: conf.key
533
- }
534
- });
535
- if (matching > 0) {
536
- stats.untouched += 1;
537
- // if configuration already exists do not overwrite it
538
- return;
539
- }
540
- stats.created += 1;
541
- await db.query('strapi::core-store').create({
542
- data: conf
543
- });
544
- }
545
- };
546
- };
547
- /**
548
- * `$ strapi configuration:restore`
549
- */ const command$l = ()=>{
550
- return commander.createCommand('configuration:restore').alias('config:restore').description('Restore configurations of your application').option('-f, --file <file>', 'Input file, default input is stdin').option('-s, --strategy <strategy>', 'Strategy name, one of: "replace", "merge", "keep"').action(runAction('configuration:restore', action$i));
551
- };
552
-
553
- const action$h = async ()=>{
554
- const appContext = await core.compileStrapi();
555
- const app = await core.createStrapi(appContext).register();
556
- const list = app.get('content-types').keys();
557
- const infoTable = new CLITable({
558
- head: [
559
- chalk.blue('Name')
560
- ]
561
- });
562
- list.forEach((name)=>infoTable.push([
563
- name
564
- ]));
565
- console.log(infoTable.toString());
566
- await app.destroy();
567
- };
568
- /**
569
- * `$ strapi content-types:list`
570
- */ const command$k = ()=>{
571
- return commander.createCommand('content-types:list').description('List all the application content-types').action(runAction('content-types:list', action$h));
572
- };
573
-
574
- const action$g = async ()=>{
575
- const appContext = await core.compileStrapi();
576
- const app = await core.createStrapi(appContext).register();
577
- const list = app.get('controllers').keys();
578
- const infoTable = new CLITable({
579
- head: [
580
- chalk.blue('Name')
581
- ]
582
- });
583
- list.forEach((name)=>infoTable.push([
584
- name
585
- ]));
586
- console.log(infoTable.toString());
587
- await app.destroy();
588
- };
589
- /**
590
- * `$ strapi controllers:list`
591
- */ const command$j = ()=>{
592
- return commander.createCommand('controllers:list').description('List all the application controllers').action(runAction('controllers:list', action$g));
593
- };
594
-
595
- const action$f = async ()=>{
596
- const appContext = await core.compileStrapi();
597
- const app = await core.createStrapi(appContext).register();
598
- const list = app.get('hooks').keys();
599
- const infoTable = new CLITable({
600
- head: [
601
- chalk.blue('Name')
602
- ]
603
- });
604
- list.forEach((name)=>infoTable.push([
605
- name
606
- ]));
607
- console.log(infoTable.toString());
608
- await app.destroy();
609
- };
610
- /**
611
- * `$ strapi hooks:list`
612
- */ const command$i = ()=>{
613
- return commander.createCommand('hooks:list').description('List all the application hooks').action(runAction('hooks:list', action$f));
614
- };
615
-
616
- const action$e = async ()=>{
617
- const appContext = await core.compileStrapi();
618
- const app = await core.createStrapi(appContext).register();
619
- const list = app.get('middlewares').keys();
620
- const infoTable = new CLITable({
621
- head: [
622
- chalk.blue('Name')
623
- ]
624
- });
625
- list.forEach((name)=>infoTable.push([
626
- name
627
- ]));
628
- console.log(infoTable.toString());
629
- await app.destroy();
630
- };
631
- /**
632
- * `$ strapi middlewares:list`
633
- */ const command$h = ()=>{
634
- return commander.createCommand('middlewares:list').description('List all the application middlewares').action(runAction('middlewares:list', action$e));
635
- };
636
-
637
- const action$d = async ()=>{
638
- const appContext = await core.compileStrapi();
639
- const app = await core.createStrapi(appContext).register();
640
- const list = app.get('policies').keys();
641
- const infoTable = new CLITable({
642
- head: [
643
- chalk.blue('Name')
644
- ]
645
- });
646
- list.forEach((name)=>infoTable.push([
647
- name
648
- ]));
649
- console.log(infoTable.toString());
650
- await app.destroy();
651
- };
652
- /**
653
- * `$ strapi policies:list`
654
- */ const command$g = ()=>{
655
- return commander.createCommand('policies:list').description('List all the application policies').action(runAction('policies:list', action$d));
656
- };
657
-
658
- const action$c = async ()=>{
659
- const appContext = await core.compileStrapi();
660
- const app = await core.createStrapi(appContext).load();
661
- const list = app.server.mount().listRoutes();
662
- const infoTable = new CLITable({
663
- head: [
664
- chalk.blue('Method'),
665
- chalk.blue('Path')
666
- ],
667
- colWidths: [
668
- 20
669
- ]
670
- });
671
- list.filter((route)=>route.methods.length).forEach((route)=>{
672
- infoTable.push([
673
- route.methods.map(fp.toUpper).join('|'),
674
- route.path
675
- ]);
676
- });
677
- console.log(infoTable.toString());
678
- await app.destroy();
679
- };
680
- /**
681
- * `$ strapi routes:list``
682
- */ const command$f = ()=>{
683
- return commander.createCommand('routes:list').description('List all the application routes').action(runAction('routes:list', action$c));
684
- };
685
-
686
- const action$b = async ()=>{
687
- const appContext = await core.compileStrapi();
688
- const app = await core.createStrapi(appContext).register();
689
- const list = app.get('services').keys();
690
- const infoTable = new CLITable({
691
- head: [
692
- chalk.blue('Name')
693
- ]
694
- });
695
- list.forEach((name)=>infoTable.push([
696
- name
697
- ]));
698
- console.log(infoTable.toString());
699
- await app.destroy();
700
- };
701
- /**
702
- * `$ strapi services:list`
703
- */ const command$e = ()=>{
704
- return commander.createCommand('services:list').description('List all the application services').action(runAction('services:list', action$b));
705
- };
706
-
707
- const sendEvent = async (event, uuid)=>{
708
- try {
709
- await fetch('https://analytics.strapi.io/api/v2/track', {
710
- method: 'POST',
711
- body: JSON.stringify({
712
- event,
713
- deviceId: utils.machineID(),
714
- groupProperties: {
715
- projectId: uuid
716
- }
717
- }),
718
- headers: {
719
- 'Content-Type': 'application/json',
720
- 'X-Strapi-Event': event
721
- }
722
- });
723
- } catch (e) {
724
- // ...
725
- }
726
- };
727
-
728
- const readPackageJSON$1 = async (path)=>{
729
- try {
730
- const packageObj = await fse.readJson(path);
731
- const uuid = packageObj.strapi ? packageObj.strapi.uuid : null;
732
- return {
733
- uuid,
734
- packageObj
735
- };
736
- } catch (err) {
737
- if (err instanceof Error) {
738
- console.error(`${chalk.red('Error')}: ${err.message}`);
739
- }
740
- }
741
- };
742
- const writePackageJSON$1 = async (path, file, spacing)=>{
743
- try {
744
- await fse.writeJson(path, file, {
745
- spaces: spacing
746
- });
747
- return true;
748
- } catch (err) {
749
- if (err instanceof Error) {
750
- console.error(`${chalk.red('Error')}: ${err.message}`);
751
- }
752
- }
753
- };
754
- const action$a = async ()=>{
755
- const packageJSONPath = path.resolve(process.cwd(), 'package.json');
756
- const exists = await fse.pathExists(packageJSONPath);
757
- if (!exists) {
758
- console.log(`${chalk.yellow('Warning')}: could not find package.json`);
759
- process.exit(0);
760
- }
761
- const { uuid, packageObj } = await readPackageJSON$1(packageJSONPath) ?? {};
762
- if (packageObj.strapi && packageObj.strapi.telemetryDisabled || !uuid) {
763
- console.log(`${chalk.yellow('Warning:')} telemetry is already disabled`);
764
- process.exit(0);
765
- }
766
- const updatedPackageJSON = {
767
- ...packageObj,
768
- strapi: {
769
- ...packageObj.strapi,
770
- telemetryDisabled: true
771
- }
772
- };
773
- const write = await writePackageJSON$1(packageJSONPath, updatedPackageJSON, 2);
774
- if (!write) {
775
- console.log(`${chalk.yellow('Warning')}: There has been an error, please set "telemetryDisabled": true in the "strapi" object of your package.json manually.`);
776
- process.exit(0);
777
- }
778
- await sendEvent('didOptOutTelemetry', uuid);
779
- console.log(`${chalk.green('Successfully opted out of Strapi telemetry')}`);
780
- process.exit(0);
781
- };
782
- /**
783
- * `$ strapi telemetry:disable`
784
- */ const command$d = ()=>{
785
- return commander.createCommand('telemetry:disable').description('Disable anonymous telemetry and metadata sending to Strapi analytics').action(runAction('telemetry:disable', action$a));
786
- };
787
-
788
- const readPackageJSON = async (path)=>{
789
- try {
790
- const packageObj = await fse.readJson(path);
791
- return packageObj;
792
- } catch (err) {
793
- if (err instanceof Error) {
794
- console.error(`${chalk.red('Error')}: ${err.message}`);
795
- } else {
796
- throw err;
797
- }
798
- }
799
- };
800
- const writePackageJSON = async (path, file, spacing)=>{
801
- try {
802
- await fse.writeJson(path, file, {
803
- spaces: spacing
804
- });
805
- return true;
806
- } catch (err) {
807
- if (err instanceof Error) {
808
- console.error(`${chalk.red('Error')}: ${err.message}`);
809
- console.log(`${chalk.yellow('Warning')}: There has been an error, please set "telemetryDisabled": false in the "strapi" object of your package.json manually.`);
810
- return false;
811
- }
812
- throw err;
813
- }
814
- };
815
- const generateNewPackageJSON = (packageObj)=>{
816
- if (!packageObj.strapi) {
817
- return {
818
- ...packageObj,
819
- strapi: {
820
- uuid: crypto.randomUUID(),
821
- telemetryDisabled: false
822
- }
823
- };
824
- }
825
- return {
826
- ...packageObj,
827
- strapi: {
828
- ...packageObj.strapi,
829
- uuid: packageObj.strapi.uuid ? packageObj.strapi.uuid : crypto.randomUUID(),
830
- telemetryDisabled: false
831
- }
832
- };
833
- };
834
- const action$9 = async ()=>{
835
- const packageJSONPath = path.resolve(process.cwd(), 'package.json');
836
- const exists = await fse.pathExists(packageJSONPath);
837
- if (!exists) {
838
- console.log(`${chalk.yellow('Warning')}: could not find package.json`);
839
- process.exit(0);
840
- }
841
- const packageObj = await readPackageJSON(packageJSONPath);
842
- if (packageObj.strapi && packageObj.strapi.uuid) {
843
- if (packageObj.strapi.telemetryDisabled === false) {
844
- console.log(`${chalk.yellow('Warning:')} telemetry is already enabled`);
845
- process.exit(0);
846
- }
847
- }
848
- const updatedPackageJSON = generateNewPackageJSON(packageObj);
849
- const write = await writePackageJSON(packageJSONPath, updatedPackageJSON, 2);
850
- if (!write) {
851
- process.exit(0);
852
- }
853
- await sendEvent('didOptInTelemetry', updatedPackageJSON.strapi.uuid);
854
- console.log(`${chalk.green('Successfully opted into and enabled Strapi telemetry')}`);
855
- process.exit(0);
856
- };
857
- /**
858
- * `$ strapi telemetry:enable`
859
- */ const command$c = ()=>{
860
- return commander.createCommand('telemetry:enable').description('Enable anonymous telemetry and metadata sending to Strapi analytics').action(runAction('telemetry:enable', action$9));
861
- };
862
-
863
- /**
864
- *`$ strapi templates:generate <directory>`
865
- */ const command$b = ()=>{
866
- return commander.createCommand('templates:generate <directory>').description('(deprecated) Generate template from Strapi project').action(()=>{
867
- console.warn('This command is deprecated and will be removed in the next major release.');
868
- console.warn('You can now copy an existing app and use it as a template.');
869
- });
870
- };
871
-
872
- const action$8 = async ({ debug, silent, verbose, outDir })=>{
873
- if ((debug || verbose) && silent) {
874
- console.error('Flags conflict: both silent and debug mode are enabled, exiting...');
875
- process.exit(1);
876
- }
877
- const appContext = await core.compileStrapi({
878
- ignoreDiagnostics: true
879
- });
880
- const app = await core.createStrapi(appContext).register();
881
- await tsUtils.generators.generate({
882
- strapi: app,
883
- pwd: appContext.appDir,
884
- rootDir: outDir ?? undefined,
885
- logger: {
886
- silent,
887
- debug
888
- },
889
- artifacts: {
890
- contentTypes: true,
891
- components: true
892
- }
893
- });
894
- await app.destroy();
895
- };
896
- /**
897
- * `$ strapi ts:generate-types`
898
- */ const command$a = ()=>{
899
- return commander.createCommand('ts:generate-types').description(`Generate TypeScript typings for your schemas`).option('-d, --debug', `Run the generation with debug messages`, false).option('-s, --silent', `Run the generation silently, without any output`, false).option('-o, --out-dir <outDir>', 'Specify a relative root directory in which the definitions will be generated. Changing this value might break types exposed by Strapi that relies on generated types.').action(runAction('ts:generate-types', action$8));
900
- };
901
-
902
- /**
903
- * @description Supports the following managers:
904
- * – npm
905
- * – yarn
906
- * – pnpm
907
- */ const getPackageManager = ()=>{
908
- // Yes, the env var is lowercase - it is set by the package managers themselves
909
- const agent = process.env.npm_config_user_agent || '';
910
- if (agent.includes('yarn')) {
911
- return 'yarn';
912
- }
913
- if (agent.includes('pnpm')) {
914
- return 'pnpm';
915
- }
916
- // Both yarn and pnpm does a `npm/?` thing, thus the slightly different match here
917
- // Theoretically not needed since we check for yarn/pnpm above, but in case other
918
- // package managers do the same thing, we'll (hopefully) catch them here.
919
- if (/^npm\/\d/.test(agent)) {
920
- return 'npm';
921
- }
922
- return undefined;
923
- };
924
-
925
- /**
926
- * From V5 this will be imported from the package.json of `@strapi/strapi`.
927
- */ const PEER_DEPS = {
928
- react: '^18.0.0',
929
- 'react-dom': '^18.0.0',
930
- 'react-router-dom': '^6.0.0',
931
- 'styled-components': '^6.0.0'
932
- };
933
- /**
934
- * Checks the user's project that it has declared and installed the required dependencies
935
- * needed by the Strapi admin project. Whilst generally speaking most modules will be
936
- * declared by the actual packages there are some packages where you only really want one of
937
- * and thus they are declared as peer dependencies – react / styled-components / etc.
938
- *
939
- * If these deps are not installed or declared, then we prompt the user to correct this. In
940
- * V4 this is not a hard requirement, but in V5 it will be. Might as well get people started now.
941
- */ const checkRequiredDependencies = async ({ cwd, logger })=>{
942
- /**
943
- * This enables us to use experimental deps for libraries like
944
- * react or styled-components. This is useful for testing against.
945
- */ if (process.env.USE_EXPERIMENTAL_DEPENDENCIES === 'true') {
946
- logger.warn('You are using experimental dependencies that may not be compatible with Strapi.');
947
- return {
948
- didInstall: false
949
- };
950
- }
951
- const pkg = await readPkgUp({
952
- cwd
953
- });
954
- if (!pkg) {
955
- throw new Error(`Could not find package.json at path: ${cwd}`);
956
- }
957
- logger.debug('Loaded package.json:', os.EOL, pkg.packageJson);
958
- /**
959
- * Run through each of the peer deps and figure out if they need to be
960
- * installed or they need their version checked against.
961
- */ const { install, review } = Object.entries(PEER_DEPS).reduce((acc, [name, version])=>{
962
- if (!pkg.packageJson.dependencies) {
963
- throw new Error(`Could not find dependencies in package.json at path: ${cwd}`);
964
- }
965
- const declaredVersion = pkg.packageJson.dependencies[name];
966
- if (!declaredVersion) {
967
- acc.install.push({
968
- name,
969
- wantedVersion: version
970
- });
971
- } else {
972
- acc.review.push({
973
- name,
974
- wantedVersion: version,
975
- declaredVersion
976
- });
977
- }
978
- return acc;
979
- }, {
980
- install: [],
981
- review: []
982
- });
983
- if (install.length > 0) {
984
- logger.info('The Strapi admin needs to install the following dependencies:', os.EOL, install.map(({ name, wantedVersion })=>` - ${name}@${wantedVersion}`).join(os.EOL));
985
- await installDependencies(install, {
986
- cwd,
987
- logger
988
- });
989
- const [file, ...args] = process.argv;
990
- /**
991
- * Re-run the same command after installation e.g. strapi build because the yarn.lock might
992
- * not be the same and could break installations. It's not the best solution, but it works.
993
- */ await execa(file, args, {
994
- cwd,
995
- stdio: 'inherit'
996
- });
997
- return {
998
- didInstall: true
999
- };
1000
- }
1001
- if (review.length) {
1002
- const errors = [];
1003
- for (const dep of review){
1004
- // The version specified in package.json could be incorrect, eg `foo`
1005
- let minDeclaredVersion = null;
1006
- try {
1007
- minDeclaredVersion = semver.minVersion(dep.declaredVersion);
1008
- } catch (err) {
1009
- // Intentional fall-through (variable will be left as null, throwing below)
1010
- }
1011
- if (!minDeclaredVersion) {
1012
- errors.push(`The declared dependency, ${dep.name} has an invalid version in package.json: ${dep.declaredVersion}`);
1013
- } else if (!semver.satisfies(minDeclaredVersion, dep.wantedVersion)) {
1014
- /**
1015
- * The delcared version should be semver compatible with our required version
1016
- * of the dependency. If it's not, we should advise the user to change it.
1017
- */ logger.warn([
1018
- `Declared version of ${dep.name} (${minDeclaredVersion}) is not compatible with the version required by Strapi (${dep.wantedVersion}).`,
1019
- 'You may experience issues, we recommend you change this.'
1020
- ].join(os.EOL));
1021
- }
1022
- const installedVersion = await getModuleVersion(dep.name, cwd);
1023
- if (!installedVersion) {
1024
- /**
1025
- * TODO: when we know the packageManager we can advise the actual install command.
1026
- */ errors.push(`The declared dependency, ${dep.name} is not installed. You should install before re-running this command`);
1027
- } else if (!semver.satisfies(installedVersion, dep.wantedVersion)) {
1028
- logger.warn([
1029
- `Declared version of ${dep.name} (${installedVersion}) is not compatible with the version required by Strapi (${dep.wantedVersion}).`,
1030
- 'You may experience issues, we recommend you change this.'
1031
- ].join(os.EOL));
1032
- }
1033
- }
1034
- if (errors.length > 0 && process.env.NODE_ENV === 'development') {
1035
- throw new Error(`${os.EOL}- ${errors.join(`${os.EOL}- `)}`);
1036
- }
1037
- }
1038
- return {
1039
- didInstall: false
1040
- };
1041
- };
1042
- const getModule = async (name, cwd)=>{
1043
- const modulePackagePath = resolveFrom.silent(cwd, path$1.join(name, 'package.json'));
1044
- if (!modulePackagePath) {
1045
- return null;
1046
- }
1047
- const file = await fs$1.readFile(modulePackagePath, 'utf8').then((res)=>JSON.parse(res));
1048
- return file;
1049
- };
1050
- const getModuleVersion = async (name, cwd)=>{
1051
- const pkg = await getModule(name, cwd);
1052
- return pkg?.version || null;
1053
- };
1054
- const installDependencies = async (install, { cwd, logger })=>{
1055
- const packageManager = getPackageManager();
1056
- if (!packageManager) {
1057
- logger.error('Could not find a supported package manager, please install the dependencies manually.');
1058
- process.exit(1);
1059
- }
1060
- const execOptions = {
1061
- encoding: 'utf8',
1062
- cwd,
1063
- stdio: 'inherit'
1064
- };
1065
- const packages = install.map(({ name, wantedVersion })=>`${name}@${wantedVersion}`);
1066
- let result;
1067
- if (packageManager === 'npm') {
1068
- const npmArgs = [
1069
- 'install',
1070
- '--legacy-peer-deps',
1071
- '--save',
1072
- ...packages
1073
- ];
1074
- logger.info(`Running 'npm ${npmArgs.join(' ')}'`);
1075
- result = await execa('npm', npmArgs, execOptions);
1076
- } else if (packageManager === 'yarn') {
1077
- const yarnArgs = [
1078
- 'add',
1079
- ...packages
1080
- ];
1081
- logger.info(`Running 'yarn ${yarnArgs.join(' ')}'`);
1082
- result = await execa('yarn', yarnArgs, execOptions);
1083
- } else if (packageManager === 'pnpm') {
1084
- const pnpmArgs = [
1085
- 'add',
1086
- '--save-prod',
1087
- ...packages
1088
- ];
1089
- logger.info(`Running 'pnpm ${pnpmArgs.join(' ')}'`);
1090
- result = await execa('pnpm', pnpmArgs, execOptions);
1091
- }
1092
- if (result?.exitCode || result?.failed) {
1093
- throw new Error('Package installation failed');
1094
- }
1095
- };
1096
-
1097
- function getTimer() {
1098
- const timings = {};
1099
- const startTimes = {};
1100
- function start(name) {
1101
- if (typeof startTimes[name] !== 'undefined') {
1102
- throw new Error(`Timer "${name}" already started, cannot overwrite`);
1103
- }
1104
- startTimes[name] = perf_hooks.performance.now();
1105
- }
1106
- function end(name) {
1107
- if (typeof startTimes[name] === 'undefined') {
1108
- throw new Error(`Timer "${name}" never started, cannot end`);
1109
- }
1110
- timings[name] = perf_hooks.performance.now() - startTimes[name];
1111
- return timings[name];
1112
- }
1113
- return {
1114
- start,
1115
- end,
1116
- getTimings: ()=>timings
1117
- };
1118
- }
1119
- const prettyTime = (timeInMs)=>{
1120
- return `${Math.ceil(timeInMs)}ms`;
1121
- };
1122
-
1123
- /**
1124
- * @internal
1125
- */ const pathExists = async (path)=>{
1126
- try {
1127
- await fs$1.access(path);
1128
- return true;
1129
- } catch (error) {
1130
- return false;
1131
- }
1132
- };
1133
- /**
1134
- * @internal
1135
- */ const loadFile = async (path)=>{
1136
- if (await pathExists(path)) {
1137
- const esbuildOptions = {
1138
- extensions: [
1139
- '.js',
1140
- '.mjs',
1141
- '.ts'
1142
- ]
1143
- };
1144
- const { unregister } = node.register(esbuildOptions);
1145
- // eslint-disable-next-line @typescript-eslint/no-var-requires
1146
- const mod = require(path);
1147
- unregister();
1148
- /**
1149
- * handles esm or cjs exporting.
1150
- */ const file = mod?.default || mod || undefined;
1151
- return file;
1152
- }
1153
- return undefined;
1154
- };
1155
- /**
1156
- * @internal
1157
- *
1158
- * @description Converts a system path to a module path mainly for `Windows` systems.
1159
- * where the path separator is `\` instead of `/`, on linux systems the path separator
1160
- * is identical to the module path separator.
1161
- */ const convertSystemPathToModulePath = (sysPath)=>{
1162
- if (process.platform === 'win32') {
1163
- return sysPath.split(path$1.sep).join(path$1.posix.sep);
1164
- }
1165
- return sysPath;
1166
- };
1167
- /**
1168
- * @internal
1169
- *
1170
- * @description Converts a module path to a system path, again largely used for Windows systems.
1171
- * The original use case was plugins where the resolve path was in module format but we want to
1172
- * have it relative to the runtime directory.
1173
- */ const convertModulePathToSystemPath = (modulePath)=>{
1174
- if (process.platform === 'win32') {
1175
- return modulePath.split(path$1.posix.sep).join(path$1.sep);
1176
- }
1177
- return modulePath;
1178
- };
1179
-
1180
- /**
1181
- * @internal
1182
- *
1183
- * @description Load the .env file if it exists
1184
- */ const loadEnv = async (cwd)=>{
1185
- const pathToEnv = path$1.resolve(cwd, '.env');
1186
- if (await pathExists(pathToEnv)) {
1187
- dotenv.config({
1188
- path: pathToEnv
1189
- });
1190
- }
1191
- };
1192
- /**
1193
- * @internal
1194
- *
1195
- * @description Get all the environment variables that start with `STRAPI_ADMIN_`
1196
- */ const getStrapiAdminEnvVars = (defaultEnv)=>{
1197
- return Object.keys(process.env).filter((key)=>key.toUpperCase().startsWith('STRAPI_ADMIN_')).reduce((acc, key)=>{
1198
- acc[key] = process.env[key];
1199
- return acc;
1200
- }, defaultEnv);
1201
- };
1202
-
1203
- const isError = (err)=>err instanceof Error;
1204
- /**
1205
- * @description Handle unexpected errors. No, but really, your CLI should anticipate error cases.
1206
- * If a user hits an error we don't expect, then we need to flag to them that this is not normal
1207
- * and they should use the `--debug` flag to get more information (assuming you've implemented this
1208
- * in your action).
1209
- */ const handleUnexpectedError = (err)=>{
1210
- console.error(chalk.red(`[ERROR] `, 'There seems to be an unexpected error, try again with --debug for more information', os.EOL));
1211
- if (isError(err) && err.stack) {
1212
- // eslint-disable-next-line no-console
1213
- console.log(chalk.red(boxen(err.stack, {
1214
- padding: 1,
1215
- align: 'left'
1216
- })));
1217
- }
1218
- if (err instanceof utils.errors.YupValidationError) {
1219
- const message = [];
1220
- const size = err.details.errors.length;
1221
- for (const error of err.details.errors){
1222
- // No need to repeat the error message as it's the same as the err.message
1223
- if (size === 1) {
1224
- message.push(` value: ${error.value}`);
1225
- continue;
1226
- }
1227
- message.push([
1228
- ` [${error.name}]`,
1229
- ` message: ${error.message}`,
1230
- ` value: ${error.value}`
1231
- ].join('\n'));
1232
- }
1233
- console.log(chalk.red(boxen([
1234
- 'Details:',
1235
- message.join('\n\n')
1236
- ].join('\n'), {
1237
- padding: 1,
1238
- align: 'left'
1239
- })));
1240
- }
1241
- process.exit(1);
1242
- };
1243
-
1244
- const validatePackageHasStrapi = (pkg)=>'strapi' in pkg && typeof pkg.strapi === 'object' && !Array.isArray(pkg.strapi) && pkg.strapi !== null;
1245
- const validatePackageIsPlugin = (pkg)=>validatePackageHasStrapi(pkg) && pkg.strapi.kind === 'plugin';
1246
- const getEnabledPlugins = async ({ cwd, logger, runtimeDir, strapi })=>{
1247
- const plugins = {};
1248
- /**
1249
- * This is the list of dependencies that are installed in the user's project.
1250
- * It will include libraries like "react", so we need to collect the ones that
1251
- * are plugins.
1252
- */ const deps = strapi.config.get('info.dependencies', {});
1253
- logger.debug("Dependencies from user's project", os.EOL, deps);
1254
- for (const dep of Object.keys(deps)){
1255
- const pkg = await getModule(dep, cwd);
1256
- if (pkg && validatePackageIsPlugin(pkg)) {
1257
- const name = pkg.strapi.name || pkg.name;
1258
- if (!name) {
1259
- /**
1260
- * Unlikely to happen, but you never know.
1261
- */ throw Error("You're trying to import a plugin that doesn't have a name – check the package.json of that plugin!");
1262
- }
1263
- plugins[name] = {
1264
- name,
1265
- importName: camelCase(name),
1266
- type: 'module',
1267
- modulePath: dep
1268
- };
1269
- }
1270
- }
1271
- const userPluginsFile = await loadUserPluginsFile(strapi.dirs.app.config);
1272
- logger.debug("User's plugins file", os.EOL, userPluginsFile);
1273
- for (const [userPluginName, userPluginConfig] of Object.entries(userPluginsFile)){
1274
- if (userPluginConfig.enabled && userPluginConfig.resolve) {
1275
- const sysPath = convertModulePathToSystemPath(userPluginConfig.resolve);
1276
- plugins[userPluginName] = {
1277
- name: userPluginName,
1278
- importName: camelCase(userPluginName),
1279
- type: 'local',
1280
- /**
1281
- * User plugin paths are resolved from the entry point
1282
- * of the app, because that's how you import them.
1283
- */ modulePath: convertSystemPathToModulePath(path$1.relative(runtimeDir, sysPath)),
1284
- path: sysPath
1285
- };
1286
- }
1287
- }
1288
- return plugins;
1289
- };
1290
- const PLUGIN_CONFIGS = [
1291
- 'plugins.js',
1292
- 'plugins.mjs',
1293
- 'plugins.ts'
1294
- ];
1295
- const loadUserPluginsFile = async (root)=>{
1296
- for (const file of PLUGIN_CONFIGS){
1297
- const filePath = path$1.join(root, file);
1298
- const configFile = await loadFile(filePath);
1299
- if (configFile) {
1300
- /**
1301
- * Configs can be a function or they can be just an object!
1302
- */ return typeof configFile === 'function' ? configFile({
1303
- env: utils.env
1304
- }) : configFile;
1305
- }
1306
- }
1307
- return {};
1308
- };
1309
- const getMapOfPluginsWithAdmin = (plugins)=>{
1310
- /**
1311
- * This variable stores the import paths for plugins.
1312
- * The keys are the module paths of the plugins, and the values are the paths
1313
- * to the admin part of the plugins, which is either loaded from the
1314
- * package.json exports or from the legacy strapi-admin.js file.
1315
- */ const pluginImportPaths = {};
1316
- return Object.values(plugins).filter((plugin)=>{
1317
- if (!plugin) {
1318
- return false;
1319
- }
1320
- /**
1321
- * There are two ways a plugin should be imported, either it's local to the strapi app,
1322
- * or it's an actual npm module that's installed and resolved via node_modules.
1323
- *
1324
- * We first check if the plugin is local to the strapi app, using a regular `fs.existsSync` because
1325
- * the pathToPlugin will be relative i.e. `/Users/my-name/strapi-app/src/plugins/my-plugin`.
1326
- *
1327
- * If the file doesn't exist well then it's probably a node_module, so instead we use `require.resolve`
1328
- * which will resolve the path to the module in node_modules. If it fails with the specific code `MODULE_NOT_FOUND`
1329
- * then it doesn't have an admin part to the package.
1330
- */ try {
1331
- const localPluginPath = plugin.path;
1332
- if (localPluginPath) {
1333
- // Here we are loading a locally installed plugin
1334
- const packageJsonPath = path$1.join(localPluginPath, 'package.json');
1335
- if (fs$2.existsSync(packageJsonPath)) {
1336
- const packageJson = JSON.parse(fs$2.readFileSync(packageJsonPath, 'utf-8'));
1337
- const localAdminPath = packageJson?.exports?.['./strapi-admin']?.import;
1338
- if (localAdminPath) {
1339
- pluginImportPaths[plugin.modulePath] = localAdminPath;
1340
- return true;
1341
- }
1342
- }
1343
- // Check if legacy admin file exists in local plugin
1344
- if (fs$2.existsSync(path$1.join(localPluginPath, 'strapi-admin.js'))) {
1345
- pluginImportPaths[plugin.modulePath] = 'strapi-admin';
1346
- return true;
1347
- }
1348
- }
1349
- // This plugin is a module, so we need to check if it has a strapi-admin export
1350
- if (require.resolve(`${plugin.modulePath}/strapi-admin`)) {
1351
- pluginImportPaths[plugin.modulePath] = 'strapi-admin';
1352
- return true;
1353
- }
1354
- return false;
1355
- } catch (err) {
1356
- if (isError(err) && 'code' in err && (err.code === 'MODULE_NOT_FOUND' || err.code === 'ERR_PACKAGE_PATH_NOT_EXPORTED')) {
1357
- /**
1358
- * the plugin does not contain FE code, so we
1359
- * don't want to import it anyway
1360
- */ return false;
1361
- }
1362
- throw err;
1363
- }
1364
- }).map((plugin)=>({
1365
- ...plugin,
1366
- modulePath: `${plugin.modulePath}/${pluginImportPaths[plugin.modulePath]}`
1367
- }));
1368
- };
1369
-
1370
- const ADMIN_APP_FILES = [
1371
- 'app.js',
1372
- 'app.mjs',
1373
- 'app.ts',
1374
- 'app.jsx',
1375
- 'app.tsx'
1376
- ];
1377
- const loadUserAppFile = async ({ runtimeDir, appDir })=>{
1378
- for (const file of ADMIN_APP_FILES){
1379
- const filePath = path$1.join(appDir, 'src', 'admin', file);
1380
- if (await pathExists(filePath)) {
1381
- return {
1382
- path: filePath,
1383
- modulePath: convertSystemPathToModulePath(path$1.relative(runtimeDir, filePath))
1384
- };
1385
- }
1386
- }
1387
- return undefined;
1388
- };
1389
-
1390
- const DEFAULT_BROWSERSLIST = [
1391
- 'last 3 major versions',
1392
- 'Firefox ESR',
1393
- 'last 2 Opera versions',
1394
- 'not dead'
1395
- ];
1396
- const createBuildContext = async ({ cwd, logger, tsconfig, strapi, options = {} })=>{
1397
- /**
1398
- * If you make a new strapi instance when one already exists,
1399
- * you will overwrite the global and the app will _most likely_
1400
- * crash and die.
1401
- */ const strapiInstance = strapi ?? core.createStrapi({
1402
- // Directories
1403
- appDir: cwd,
1404
- distDir: tsconfig?.config.options.outDir ?? '',
1405
- // Options
1406
- autoReload: true,
1407
- serveAdminPanel: false
1408
- });
1409
- const serverAbsoluteUrl = strapiInstance.config.get('server.absoluteUrl');
1410
- const adminAbsoluteUrl = strapiInstance.config.get('admin.absoluteUrl');
1411
- const adminPath = strapiInstance.config.get('admin.path');
1412
- // NOTE: Checks that both the server and admin will be served from the same origin (protocol, host, port)
1413
- const sameOrigin = new URL(adminAbsoluteUrl).origin === new URL(serverAbsoluteUrl).origin;
1414
- const adminPublicPath = new URL(adminAbsoluteUrl).pathname;
1415
- const serverPublicPath = new URL(serverAbsoluteUrl).pathname;
1416
- const appDir = strapiInstance.dirs.app.root;
1417
- await loadEnv(cwd);
1418
- const env = getStrapiAdminEnvVars({
1419
- ADMIN_PATH: adminPublicPath,
1420
- STRAPI_ADMIN_BACKEND_URL: sameOrigin ? serverPublicPath : serverAbsoluteUrl,
1421
- STRAPI_TELEMETRY_DISABLED: String(strapiInstance.telemetry.isDisabled)
1422
- });
1423
- const envKeys = Object.keys(env);
1424
- if (envKeys.length > 0) {
1425
- logger.info([
1426
- 'Including the following ENV variables as part of the JS bundle:',
1427
- ...envKeys.map((key)=>` - ${key}`)
1428
- ].join(os.EOL));
1429
- }
1430
- const distPath = path$1.join(strapiInstance.dirs.dist.root, 'build');
1431
- const distDir = path$1.relative(cwd, distPath);
1432
- /**
1433
- * If the distPath already exists, clean it
1434
- */ try {
1435
- logger.debug(`Cleaning dist folder: ${distPath}`);
1436
- await fs$1.rm(distPath, {
1437
- recursive: true,
1438
- force: true
1439
- });
1440
- logger.debug('Cleaned dist folder');
1441
- } catch {
1442
- // do nothing, it will fail if the folder does not exist
1443
- logger.debug('There was no dist folder to clean');
1444
- }
1445
- const runtimeDir = path$1.join(cwd, '.strapi', 'client');
1446
- const entry = path$1.relative(cwd, path$1.join(runtimeDir, 'app.js'));
1447
- const plugins = await getEnabledPlugins({
1448
- cwd,
1449
- logger,
1450
- runtimeDir,
1451
- strapi: strapiInstance
1452
- });
1453
- logger.debug('Enabled plugins', os.EOL, plugins);
1454
- const pluginsWithFront = getMapOfPluginsWithAdmin(plugins);
1455
- logger.debug('Enabled plugins with FE', os.EOL, pluginsWithFront);
1456
- const target = browserslist.loadConfig({
1457
- path: cwd
1458
- }) ?? DEFAULT_BROWSERSLIST;
1459
- const customisations = await loadUserAppFile({
1460
- appDir,
1461
- runtimeDir
1462
- });
1463
- const features = strapiInstance.config.get('features', undefined);
1464
- const { bundler = 'vite', ...restOptions } = options;
1465
- const buildContext = {
1466
- appDir,
1467
- adminPath,
1468
- basePath: adminPublicPath,
1469
- bundler,
1470
- customisations,
1471
- cwd,
1472
- distDir,
1473
- distPath,
1474
- entry,
1475
- env,
1476
- features,
1477
- logger,
1478
- options: restOptions,
1479
- plugins: pluginsWithFront,
1480
- runtimeDir,
1481
- strapi: strapiInstance,
1482
- target,
1483
- tsconfig
1484
- };
1485
- return buildContext;
1486
- };
1487
-
1488
- const getEntryModule = (ctx)=>{
1489
- const pluginsObject = ctx.plugins.map(({ name, importName })=>`'${name}': ${importName}`).join(',\n');
1490
- const pluginsImport = ctx.plugins.map(({ importName, modulePath })=>`import ${importName} from '${modulePath}';`).join('\n');
1491
- return outdent`
1492
- /**
1493
- * This file was automatically generated by Strapi.
1494
- * Any modifications made will be discarded.
1495
- */
1496
- ${pluginsImport}
1497
- import { renderAdmin } from "@strapi/strapi/admin"
1498
-
1499
- ${ctx.customisations?.modulePath ? `import customisations from '${ctx.customisations.modulePath}'` : ''}
1500
-
1501
- renderAdmin(
1502
- document.getElementById("strapi"),
1503
- {
1504
- ${ctx.customisations?.modulePath ? 'customisations,' : ''}
1505
- ${ctx.features ? `features: ${JSON.stringify(ctx.features)},` : ''}
1506
- plugins: {
1507
- ${pluginsObject}
1508
- }
1509
- })
1510
- `;
1511
- };
1512
- /**
1513
- * TODO: Here in the future we could add the ability
1514
- * to load a user's Document component?
1515
- */ const getDocumentHTML = ({ logger, props = {} })=>{
1516
- const result = server.renderToStaticMarkup(react.createElement(_internal.DefaultDocument, props));
1517
- logger.debug('Rendered the HTML');
1518
- return outdent`<!DOCTYPE html>${result}`;
1519
- };
1520
- const AUTO_GENERATED_WARNING = `
1521
- This file was automatically generated by Strapi.
1522
- Any modifications made will be discarded.
1523
- `.trim();
1524
- /**
1525
- * Because we now auto-generate the index.html file,
1526
- * we should be clear that people _should not_ modify it.
1527
- *
1528
- * @internal
1529
- */ const decorateHTMLWithAutoGeneratedWarning = (htmlTemplate)=>htmlTemplate.replace(/<head/, `\n<!--\n${AUTO_GENERATED_WARNING}\n-->\n<head`);
1530
- const writeStaticClientFiles = async (ctx)=>{
1531
- const prettier = await import('prettier'); // ESM-only
1532
- /**
1533
- * For everything to work effectively we create a client folder in `.strapi` at the cwd level.
1534
- * We then use the function we need to "createAdmin" as well as generate the Document index.html as well.
1535
- *
1536
- * All this links together an imaginary "src/index" that then allows vite to correctly build the admin panel.
1537
- */ await fs$1.mkdir(ctx.runtimeDir, {
1538
- recursive: true
1539
- });
1540
- ctx.logger.debug('Created the runtime directory');
1541
- const indexHtml = decorateHTMLWithAutoGeneratedWarning(await getDocumentHTML({
1542
- logger: ctx.logger,
1543
- props: ctx.bundler === 'vite' ? {
1544
- entryPath: `/${ctx.entry}`
1545
- } : undefined
1546
- }));
1547
- await fs$1.writeFile(path$1.join(ctx.runtimeDir, 'index.html'), await prettier.format(indexHtml, {
1548
- parser: 'html'
1549
- }));
1550
- ctx.logger.debug('Wrote the index.html file');
1551
- await fs$1.writeFile(path$1.join(ctx.runtimeDir, 'app.js'), await prettier.format(getEntryModule(ctx), {
1552
- parser: 'babel'
1553
- }));
1554
- ctx.logger.debug('Wrote the app.js file');
1555
- };
1556
-
1557
- /**
1558
- * @example `$ strapi build`
1559
- *
1560
- * @description Builds the admin panel of the strapi application.
1561
- */ const build = async ({ logger, cwd, tsconfig, ...options })=>{
1562
- const timer = getTimer();
1563
- const { didInstall } = await checkRequiredDependencies({
1564
- cwd,
1565
- logger
1566
- }).catch((err)=>{
1567
- logger.error(err.message);
1568
- process.exit(1);
1569
- });
1570
- if (didInstall) {
1571
- return;
1572
- }
1573
- if (tsconfig?.config) {
1574
- timer.start('compilingTS');
1575
- const compilingTsSpinner = logger.spinner(`Compiling TS`).start();
1576
- tsUtils__namespace.compile(cwd, {
1577
- configOptions: {
1578
- ignoreDiagnostics: false
1579
- }
1580
- });
1581
- const compilingDuration = timer.end('compilingTS');
1582
- compilingTsSpinner.text = `Compiling TS (${prettyTime(compilingDuration)})`;
1583
- compilingTsSpinner.succeed();
1584
- }
1585
- timer.start('createBuildContext');
1586
- const contextSpinner = logger.spinner(`Building build context`).start();
1587
- console.log('');
1588
- const ctx = await createBuildContext({
1589
- cwd,
1590
- logger,
1591
- tsconfig,
1592
- options
1593
- });
1594
- const contextDuration = timer.end('createBuildContext');
1595
- contextSpinner.text = `Building build context (${prettyTime(contextDuration)})`;
1596
- contextSpinner.succeed();
1597
- timer.start('buildAdmin');
1598
- const buildingSpinner = logger.spinner(`Building admin panel`).start();
1599
- console.log('');
1600
- try {
1601
- await writeStaticClientFiles(ctx);
1602
- if (ctx.bundler === 'webpack') {
1603
- const { build: buildWebpack } = await Promise.resolve().then(function () { return require('./build-XY97xrwm.js'); });
1604
- await buildWebpack(ctx);
1605
- } else if (ctx.bundler === 'vite') {
1606
- const { build: buildVite } = await Promise.resolve().then(function () { return require('./build-RZ8_oqFy.js'); });
1607
- await buildVite(ctx);
1608
- }
1609
- const buildDuration = timer.end('buildAdmin');
1610
- buildingSpinner.text = `Building admin panel (${prettyTime(buildDuration)})`;
1611
- buildingSpinner.succeed();
1612
- } catch (err) {
1613
- buildingSpinner.fail();
1614
- throw err;
1615
- }
1616
- };
1617
-
1618
- const action$7 = async (options)=>{
1619
- try {
1620
- if (options.bundler === 'webpack') {
1621
- options.logger.warn('[@strapi/strapi]: Using webpack as a bundler is deprecated. You should migrate to vite.');
1622
- }
1623
- await build(options);
1624
- } catch (err) {
1625
- handleUnexpectedError(err);
1626
- }
1627
- };
1628
- /**
1629
- * `$ strapi build`
1630
- */ const command$9 = ({ ctx })=>{
1631
- return commander.createCommand('build').option('--bundler [bundler]', 'Bundler to use (webpack or vite)', 'vite').option('-d, --debug', 'Enable debugging mode with verbose logs', false).option('--minify', 'Minify the output', true).option('--silent', "Don't log anything", false).option('--sourcemap', 'Produce sourcemaps', false).option('--stats', 'Print build statistics to the console', false).description('Build the strapi admin app').action(async (options)=>{
1632
- return action$7({
1633
- ...options,
1634
- ...ctx
1635
- });
1636
- });
1637
- };
1638
-
1639
- const action$6 = async ()=>{
1640
- const appContext = await core.compileStrapi();
1641
- const app = await core.createStrapi(appContext).load();
1642
- app.start().then(()=>{
1643
- const repl = REPL.start(app.config.info.name + ' > ' || 'strapi > '); // eslint-disable-line prefer-template
1644
- repl.on('exit', (err)=>{
1645
- if (err) {
1646
- app.log.error(err);
1647
- process.exit(1);
1648
- }
1649
- app.server.destroy();
1650
- process.exit(0);
1651
- });
1652
- });
1653
- };
1654
- /**
1655
- * `$ strapi console`
1656
- */ const command$8 = ()=>{
1657
- return commander.createCommand('console').description('Open the Strapi framework console').action(runAction('console', action$6));
1658
- };
1659
-
1660
- // This method removes all non-admin build files from the dist directory
1661
- const cleanupDistDirectory = async ({ tsconfig, logger, timer })=>{
1662
- const distDir = tsconfig?.config?.options?.outDir;
1663
- if (!distDir || // we don't have a dist dir
1664
- await fs$1.access(distDir).then(()=>false).catch(()=>true) // it doesn't exist -- if it does but no access, that will be caught later
1665
- ) {
1666
- return;
1667
- }
1668
- const timerName = `cleaningDist${Date.now()}`;
1669
- timer.start(timerName);
1670
- const cleaningSpinner = logger.spinner(`Cleaning dist dir ${distDir}`).start();
1671
- try {
1672
- const dirContent = await fs$1.readdir(distDir);
1673
- const validFilenames = dirContent// Ignore the admin build folder
1674
- .filter((filename)=>filename !== 'build');
1675
- for (const filename of validFilenames){
1676
- await fs$1.rm(path$1.resolve(distDir, filename), {
1677
- recursive: true
1678
- });
1679
- }
1680
- } catch (err) {
1681
- const generatingDuration = timer.end(timerName);
1682
- cleaningSpinner.text = `Error cleaning dist dir: ${err} (${prettyTime(generatingDuration)})`;
1683
- cleaningSpinner?.fail();
1684
- return;
1685
- }
1686
- const generatingDuration = timer.end(timerName);
1687
- cleaningSpinner.text = `Cleaning dist dir (${prettyTime(generatingDuration)})`;
1688
- cleaningSpinner?.succeed();
1689
- };
1690
- const develop = async ({ cwd, polling, logger, tsconfig, watchAdmin, ...options })=>{
1691
- const timer = getTimer();
1692
- if (cluster.isPrimary) {
1693
- const { didInstall } = await checkRequiredDependencies({
1694
- cwd,
1695
- logger
1696
- }).catch((err)=>{
1697
- logger.error(err.message);
1698
- process.exit(1);
1699
- });
1700
- if (didInstall) {
1701
- return;
1702
- }
1703
- if (tsconfig?.config) {
1704
- // Build without diagnostics in case schemas have changed
1705
- await cleanupDistDirectory({
1706
- tsconfig,
1707
- logger,
1708
- timer
1709
- });
1710
- await tsUtils__namespace.compile(cwd, {
1711
- configOptions: {
1712
- ignoreDiagnostics: true
1713
- }
1714
- });
1715
- }
1716
- /**
1717
- * IF we're not watching the admin we're going to build it, this makes
1718
- * sure that at least the admin is built for users & they can interact
1719
- * with the application.
1720
- */ if (!watchAdmin) {
1721
- timer.start('createBuildContext');
1722
- const contextSpinner = logger.spinner(`Building build context`).start();
1723
- console.log('');
1724
- const ctx = await createBuildContext({
1725
- cwd,
1726
- logger,
1727
- tsconfig,
1728
- options
1729
- });
1730
- const contextDuration = timer.end('createBuildContext');
1731
- contextSpinner.text = `Building build context (${prettyTime(contextDuration)})`;
1732
- contextSpinner.succeed();
1733
- timer.start('creatingAdmin');
1734
- const adminSpinner = logger.spinner(`Creating admin`).start();
1735
- await writeStaticClientFiles(ctx);
1736
- if (ctx.bundler === 'webpack') {
1737
- const { build: buildWebpack } = await Promise.resolve().then(function () { return require('./build-XY97xrwm.js'); });
1738
- await buildWebpack(ctx);
1739
- } else if (ctx.bundler === 'vite') {
1740
- const { build: buildVite } = await Promise.resolve().then(function () { return require('./build-RZ8_oqFy.js'); });
1741
- await buildVite(ctx);
1742
- }
1743
- const adminDuration = timer.end('creatingAdmin');
1744
- adminSpinner.text = `Creating admin (${prettyTime(adminDuration)})`;
1745
- adminSpinner.succeed();
1746
- }
1747
- cluster.on('message', async (worker, message)=>{
1748
- switch(message){
1749
- case 'reload':
1750
- {
1751
- if (tsconfig?.config) {
1752
- // Build without diagnostics in case schemas have changed
1753
- await cleanupDistDirectory({
1754
- tsconfig,
1755
- logger,
1756
- timer
1757
- });
1758
- await tsUtils__namespace.compile(cwd, {
1759
- configOptions: {
1760
- ignoreDiagnostics: true
1761
- }
1762
- });
1763
- }
1764
- logger.debug('cluster has the reload message, sending the worker kill message');
1765
- worker.send('kill');
1766
- break;
1767
- }
1768
- case 'killed':
1769
- {
1770
- logger.debug('cluster has the killed message, forking the cluster');
1771
- cluster.fork();
1772
- break;
1773
- }
1774
- case 'stop':
1775
- {
1776
- process.exit(1);
1777
- break;
1778
- }
1779
- }
1780
- });
1781
- cluster.fork();
1782
- }
1783
- if (cluster.isWorker) {
1784
- timer.start('loadStrapi');
1785
- const loadStrapiSpinner = logger.spinner(`Loading Strapi`).start();
1786
- const strapi = core.createStrapi({
1787
- appDir: cwd,
1788
- distDir: tsconfig?.config.options.outDir ?? '',
1789
- autoReload: true,
1790
- serveAdminPanel: !watchAdmin
1791
- });
1792
- /**
1793
- * If we're watching the admin panel then we're going to attach the watcher
1794
- * as a strapi middleware.
1795
- */ let bundleWatcher;
1796
- const strapiInstance = await strapi.load();
1797
- if (watchAdmin) {
1798
- timer.start('createBuildContext');
1799
- const contextSpinner = logger.spinner(`Building build context`).start();
1800
- console.log('');
1801
- const ctx = await createBuildContext({
1802
- cwd,
1803
- logger,
1804
- strapi,
1805
- tsconfig,
1806
- options
1807
- });
1808
- const contextDuration = timer.end('createBuildContext');
1809
- contextSpinner.text = `Building build context (${prettyTime(contextDuration)})`;
1810
- contextSpinner.succeed();
1811
- timer.start('creatingAdmin');
1812
- const adminSpinner = logger.spinner(`Creating admin`).start();
1813
- await writeStaticClientFiles(ctx);
1814
- if (ctx.bundler === 'webpack') {
1815
- const { watch: watchWebpack } = await Promise.resolve().then(function () { return require('./watch-BKW3b7Rj.js'); });
1816
- bundleWatcher = await watchWebpack(ctx);
1817
- } else if (ctx.bundler === 'vite') {
1818
- const { watch: watchVite } = await Promise.resolve().then(function () { return require('./watch-DzwyHBm4.js'); });
1819
- bundleWatcher = await watchVite(ctx);
1820
- }
1821
- const adminDuration = timer.end('creatingAdmin');
1822
- adminSpinner.text = `Creating admin (${prettyTime(adminDuration)})`;
1823
- adminSpinner.succeed();
1824
- }
1825
- const loadStrapiDuration = timer.end('loadStrapi');
1826
- loadStrapiSpinner.text = `Loading Strapi (${prettyTime(loadStrapiDuration)})`;
1827
- loadStrapiSpinner.succeed();
1828
- // For TS projects, type generation is a requirement for the develop command so that the server can restart
1829
- // For JS projects, we respect the experimental autogenerate setting
1830
- if (tsconfig?.config || strapi.config.get('typescript.autogenerate') !== false) {
1831
- timer.start('generatingTS');
1832
- const generatingTsSpinner = logger.spinner(`Generating types`).start();
1833
- await tsUtils__namespace.generators.generate({
1834
- strapi: strapiInstance,
1835
- pwd: cwd,
1836
- rootDir: undefined,
1837
- logger: {
1838
- silent: true,
1839
- debug: false
1840
- },
1841
- artifacts: {
1842
- contentTypes: true,
1843
- components: true
1844
- }
1845
- });
1846
- const generatingDuration = timer.end('generatingTS');
1847
- generatingTsSpinner.text = `Generating types (${prettyTime(generatingDuration)})`;
1848
- generatingTsSpinner.succeed();
1849
- }
1850
- if (tsconfig?.config) {
1851
- timer.start('compilingTS');
1852
- const compilingTsSpinner = logger.spinner(`Compiling TS`).start();
1853
- await cleanupDistDirectory({
1854
- tsconfig,
1855
- logger,
1856
- timer
1857
- });
1858
- await tsUtils__namespace.compile(cwd, {
1859
- configOptions: {
1860
- ignoreDiagnostics: false
1861
- }
1862
- });
1863
- const compilingDuration = timer.end('compilingTS');
1864
- compilingTsSpinner.text = `Compiling TS (${prettyTime(compilingDuration)})`;
1865
- compilingTsSpinner.succeed();
1866
- }
1867
- const restart = async ()=>{
1868
- if (strapiInstance.reload.isWatching && !strapiInstance.reload.isReloading) {
1869
- strapiInstance.reload.isReloading = true;
1870
- strapiInstance.reload();
1871
- }
1872
- };
1873
- const watcher = chokidar.watch(cwd, {
1874
- ignoreInitial: true,
1875
- usePolling: polling,
1876
- ignored: [
1877
- /(^|[/\\])\../,
1878
- /tmp/,
1879
- '**/src/admin/**',
1880
- '**/src/plugins/**/admin/**',
1881
- '**/dist/src/plugins/test/admin/**',
1882
- '**/documentation',
1883
- '**/documentation/**',
1884
- '**/node_modules',
1885
- '**/node_modules/**',
1886
- '**/plugins.json',
1887
- '**/build',
1888
- '**/build/**',
1889
- '**/log',
1890
- '**/log/**',
1891
- '**/logs',
1892
- '**/logs/**',
1893
- '**/*.log',
1894
- '**/index.html',
1895
- '**/public',
1896
- '**/public/**',
1897
- strapiInstance.dirs.static.public,
1898
- utils.strings.joinBy('/', strapiInstance.dirs.static.public, '**'),
1899
- '**/*.db*',
1900
- '**/exports/**',
1901
- '**/dist/**',
1902
- '**/*.d.ts',
1903
- '**/.yalc/**',
1904
- '**/yalc.lock',
1905
- // TODO v6: watch only src folder by default, and flip this to watchIncludeFiles
1906
- ...strapiInstance.config.get('admin.watchIgnoreFiles', [])
1907
- ]
1908
- }).on('add', (path)=>{
1909
- strapiInstance.log.info(`File created: ${path}`);
1910
- restart();
1911
- }).on('change', (path)=>{
1912
- strapiInstance.log.info(`File changed: ${path}`);
1913
- restart();
1914
- }).on('unlink', (path)=>{
1915
- strapiInstance.log.info(`File deleted: ${path}`);
1916
- restart();
1917
- });
1918
- process.on('message', async (message)=>{
1919
- switch(message){
1920
- case 'kill':
1921
- {
1922
- logger.debug('child process has the kill message, destroying the strapi instance and sending the killed process message');
1923
- await watcher.close();
1924
- await strapiInstance.destroy();
1925
- if (bundleWatcher) {
1926
- bundleWatcher.close();
1927
- }
1928
- process.send?.('killed');
1929
- break;
1930
- }
1931
- }
1932
- });
1933
- strapiInstance.start();
1934
- }
1935
- };
1936
-
1937
- const action$5 = async (options)=>{
1938
- try {
1939
- if (cluster.isPrimary) {
1940
- if (options.bundler === 'webpack') {
1941
- options.logger.warn('[@strapi/strapi]: Using webpack as a bundler is deprecated. You should migrate to vite.');
1942
- }
1943
- }
1944
- await develop(options);
1945
- } catch (err) {
1946
- handleUnexpectedError(err);
1947
- }
1948
- };
1949
- /**
1950
- * `$ strapi develop`
1951
- */ const command$7 = ({ ctx })=>{
1952
- return commander.createCommand('develop').alias('dev').option('--bundler [bundler]', 'Bundler to use (webpack or vite)', 'vite').option('-d, --debug', 'Enable debugging mode with verbose logs', false).option('--silent', "Don't log anything", false).option('--polling', 'Watch for file changes in network directories', false).option('--watch-admin', 'Watch the admin panel for hot changes', true).option('--no-watch-admin', 'Do not watch the admin panel for hot changes').option('--open', 'Open the admin in your browser', true).description('Start your Strapi application in development mode').action(async (options)=>{
1953
- return action$5({
1954
- ...options,
1955
- ...ctx
1956
- });
1957
- });
1958
- };
1959
-
1960
- /**
1961
- * `$ strapi generate`
1962
- */ const command$6 = ({ argv })=>{
1963
- return commander.createCommand('generate').description('Launch the interactive API generator').action(()=>{
1964
- assertCwdContainsStrapiProject('generate');
1965
- argv.splice(2, 1);
1966
- // NOTE: this needs to be lazy loaded in order for plop to work correctly
1967
- import('@strapi/generators').then((gen)=>gen.runCLI());
1968
- });
1969
- };
1970
-
1971
- const action$4 = async ({ uuid, dependencies, all })=>{
1972
- const config = {
1973
- reportUUID: Boolean(all || uuid),
1974
- reportDependencies: Boolean(all || dependencies)
1975
- };
1976
- const appContext = await core.compileStrapi();
1977
- const app = await core.createStrapi(appContext).register();
1978
- let debugInfo = `Launched In: ${Date.now() - app.config.launchedAt} ms
1979
- Environment: ${app.config.environment}
1980
- OS: ${process.platform}-${process.arch}
1981
- Strapi Version: ${app.config.info.strapi}
1982
- Node/Yarn Version: ${process.env.npm_config_user_agent}
1983
- Edition: ${app.EE ? 'Enterprise' : 'Community'}
1984
- Database: ${app?.config?.database?.connection?.client ?? 'unknown'}`;
1985
- if (config.reportUUID) {
1986
- debugInfo += `${os$1.EOL}UUID: ${app.config.uuid}`;
1987
- }
1988
- if (config.reportDependencies) {
1989
- debugInfo += `${os$1.EOL}Dependencies: ${JSON.stringify(app.config.info.dependencies, null, 2)}
1990
- Dev Dependencies: ${JSON.stringify(app.config.info.devDependencies, null, 2)}`;
1991
- }
1992
- console.log(debugInfo);
1993
- await app.destroy();
1994
- };
1995
- /**
1996
- * `$ strapi report`
1997
- */ const command$5 = ()=>{
1998
- return commander.createCommand('report').description('Get system stats for debugging and submitting issues').option('-u, --uuid', 'Include Project UUID').option('-d, --dependencies', 'Include Project Dependencies').option('--all', 'Include All Information').action(runAction('report', action$4));
1999
- };
2000
-
2001
- const action$3 = async ()=>{
2002
- const appDir = process.cwd();
2003
- const isTSProject = await tsUtils.isUsingTypeScript(appDir);
2004
- const outDir = await tsUtils.resolveOutDir(appDir);
2005
- const distDir = isTSProject ? outDir : appDir;
2006
- const buildDirExists = fs.existsSync(outDir);
2007
- if (isTSProject && !buildDirExists) throw new Error(`${outDir} directory not found. Please run the build command before starting your application`);
2008
- core.createStrapi({
2009
- appDir,
2010
- distDir
2011
- }).start();
2012
- };
2013
- /**
2014
- * `$ strapi start`
2015
- */ const command$4 = ()=>{
2016
- return commander.createCommand('start').description('Start your Strapi application').action(runAction('start', action$3));
2017
- };
2018
-
2019
- var version = "5.11.3";
2020
-
2021
- /**
2022
- * `$ strapi version`
2023
- */ const command$3 = ()=>{
2024
- // load the Strapi package.json to get version and other information
2025
- return commander.createCommand('version').description('Output the version of Strapi').action(()=>{
2026
- process.stdout.write(`${version}\n`);
2027
- process.exit(0);
2028
- });
2029
- };
2030
-
2031
- /**
2032
- * argParser: Parse a comma-delimited string as an array
2033
- */ const parseList = (value)=>{
2034
- try {
2035
- return value.split(',').map((item)=>item.trim()); // trim shouldn't be necessary but might help catch unexpected whitespace characters
2036
- } catch (e) {
2037
- exitWith(1, `Unrecognized input: ${value}`);
2038
- }
2039
- return [];
2040
- };
2041
- /**
2042
- * Returns an argParser that returns a list
2043
- */ const getParseListWithChoices = (choices, errorMessage = 'Invalid options:')=>{
2044
- return (value)=>{
2045
- const list = parseList(value);
2046
- const invalid = list.filter((item)=>{
2047
- return !choices.includes(item);
2048
- });
2049
- if (invalid.length > 0) {
2050
- exitWith(1, `${errorMessage}: ${invalid.join(',')}`);
2051
- }
2052
- return list;
2053
- };
2054
- };
2055
- /**
2056
- * argParser: Parse a string as an integer
2057
- */ const parseInteger = (value)=>{
2058
- // parseInt takes a string and a radix
2059
- const parsedValue = parseInt(value, 10);
2060
- if (fp.isNaN(parsedValue)) {
2061
- throw new commander.InvalidOptionArgumentError(`Not an integer: ${value}`);
2062
- }
2063
- return parsedValue;
2064
- };
2065
- /**
2066
- * argParser: Parse a string as a URL object
2067
- */ const parseURL = (value)=>{
2068
- try {
2069
- const url = new URL(value);
2070
- if (!url.host) {
2071
- throw new commander.InvalidOptionArgumentError(`Could not parse url ${value}`);
2072
- }
2073
- return url;
2074
- } catch (e) {
2075
- throw new commander.InvalidOptionArgumentError(`Could not parse url ${value}`);
2076
- }
2077
- };
2078
- /**
2079
- * hook: if encrypt==true and key not provided, prompt for it
2080
- */ const promptEncryptionKey = async (thisCommand)=>{
2081
- const opts = thisCommand.opts();
2082
- if (!opts.encrypt && opts.key) {
2083
- return exitWith(1, 'Key may not be present unless encryption is used');
2084
- }
2085
- // if encrypt==true but we have no key, prompt for it
2086
- if (opts.encrypt && !(opts.key && opts.key.length > 0)) {
2087
- try {
2088
- const answers = await inquirer.prompt([
2089
- {
2090
- type: 'password',
2091
- message: 'Please enter an encryption key',
2092
- name: 'key',
2093
- validate (key) {
2094
- if (key.length > 0) return true;
2095
- return 'Key must be present when using the encrypt option';
2096
- }
2097
- }
2098
- ]);
2099
- opts.key = answers.key;
2100
- } catch (e) {
2101
- return exitWith(1, 'Failed to get encryption key');
2102
- }
2103
- if (!opts.key) {
2104
- return exitWith(1, 'Failed to get encryption key');
2105
- }
2106
- }
2107
- };
2108
- /**
2109
- * hook: require a confirmation message to be accepted unless forceOption (-f,--force) is used
2110
- */ const getCommanderConfirmMessage = (message, { failMessage } = {})=>{
2111
- return async (command)=>{
2112
- const confirmed = await confirmMessage(message, {
2113
- force: command.opts().force
2114
- });
2115
- if (!confirmed) {
2116
- exitWith(1, failMessage);
2117
- }
2118
- };
2119
- };
2120
- const confirmMessage = async (message, { force } = {})=>{
2121
- // if we have a force option, respond yes
2122
- if (force === true) {
2123
- // attempt to mimic the inquirer prompt exactly
2124
- console.log(`${chalk.green('?')} ${chalk.bold(message)} ${chalk.cyan('Yes')}`);
2125
- return true;
2126
- }
2127
- const answers = await inquirer.prompt([
2128
- {
2129
- type: 'confirm',
2130
- message,
2131
- name: `confirm`,
2132
- default: false
2133
- }
2134
- ]);
2135
- return answers.confirm;
2136
- };
2137
- const forceOption = new commander.Option('--force', `Automatically answer "yes" to all prompts, including potentially destructive requests, and run non-interactively.`);
2138
-
2139
- const { errors: { TransferEngineInitializationError } } = dataTransfer.engine;
2140
- const exitMessageText = (process1, error = false)=>{
2141
- const processCapitalized = process1[0].toUpperCase() + process1.slice(1);
2142
- if (!error) {
2143
- return chalk.bold(chalk.green(`${processCapitalized} process has been completed successfully!`));
2144
- }
2145
- return chalk.bold(chalk.red(`${processCapitalized} process failed.`));
2146
- };
2147
- const pad = (n)=>{
2148
- return (n < 10 ? '0' : '') + String(n);
2149
- };
2150
- const yyyymmddHHMMSS = ()=>{
2151
- const date = new Date();
2152
- return date.getFullYear() + pad(date.getMonth() + 1) + pad(date.getDate()) + pad(date.getHours()) + pad(date.getMinutes()) + pad(date.getSeconds());
2153
- };
2154
- const getDefaultExportName = ()=>{
2155
- return `export_${yyyymmddHHMMSS()}`;
2156
- };
2157
- const buildTransferTable = (resultData)=>{
2158
- if (!resultData) {
2159
- return;
2160
- }
2161
- // Build pretty table
2162
- const table = new CLITable({
2163
- head: [
2164
- 'Type',
2165
- 'Count',
2166
- 'Size'
2167
- ].map((text)=>chalk.bold.blue(text))
2168
- });
2169
- let totalBytes = 0;
2170
- let totalItems = 0;
2171
- Object.keys(resultData).forEach((stage)=>{
2172
- const item = resultData[stage];
2173
- if (!item) {
2174
- return;
2175
- }
2176
- table.push([
2177
- {
2178
- hAlign: 'left',
2179
- content: chalk.bold(stage)
2180
- },
2181
- {
2182
- hAlign: 'right',
2183
- content: item.count
2184
- },
2185
- {
2186
- hAlign: 'right',
2187
- content: `${readableBytes(item.bytes, 1, 11)} `
2188
- }
2189
- ]);
2190
- totalBytes += item.bytes;
2191
- totalItems += item.count;
2192
- if (item.aggregates) {
2193
- Object.keys(item.aggregates).sort().forEach((subkey)=>{
2194
- if (!item.aggregates) {
2195
- return;
2196
- }
2197
- const subitem = item.aggregates[subkey];
2198
- table.push([
2199
- {
2200
- hAlign: 'left',
2201
- content: `-- ${chalk.bold.grey(subkey)}`
2202
- },
2203
- {
2204
- hAlign: 'right',
2205
- content: chalk.grey(subitem.count)
2206
- },
2207
- {
2208
- hAlign: 'right',
2209
- content: chalk.grey(`(${readableBytes(subitem.bytes, 1, 11)})`)
2210
- }
2211
- ]);
2212
- });
2213
- }
2214
- });
2215
- table.push([
2216
- {
2217
- hAlign: 'left',
2218
- content: chalk.bold.green('Total')
2219
- },
2220
- {
2221
- hAlign: 'right',
2222
- content: chalk.bold.green(totalItems)
2223
- },
2224
- {
2225
- hAlign: 'right',
2226
- content: `${chalk.bold.green(readableBytes(totalBytes, 1, 11))} `
2227
- }
2228
- ]);
2229
- return table;
2230
- };
2231
- const DEFAULT_IGNORED_CONTENT_TYPES = [
2232
- 'admin::permission',
2233
- 'admin::user',
2234
- 'admin::role',
2235
- 'admin::api-token',
2236
- 'admin::api-token-permission',
2237
- 'admin::transfer-token',
2238
- 'admin::transfer-token-permission',
2239
- 'admin::audit-log',
2240
- 'plugin::content-releases.release',
2241
- 'plugin::content-releases.release-action'
2242
- ];
2243
- const abortTransfer = async ({ engine, strapi: strapi1 })=>{
2244
- try {
2245
- await engine.abortTransfer();
2246
- await strapi1.destroy();
2247
- } catch (e) {
2248
- // ignore because there's not much else we can do
2249
- return false;
2250
- }
2251
- return true;
2252
- };
2253
- const setSignalHandler = async (handler, signals = [
2254
- 'SIGINT',
2255
- 'SIGTERM',
2256
- 'SIGQUIT'
2257
- ])=>{
2258
- signals.forEach((signal)=>{
2259
- // We specifically remove ALL listeners because we have to clear the one added in Strapi bootstrap that has a process.exit
2260
- // TODO: Ideally Strapi bootstrap would not add that listener, and then this could be more flexible and add/remove only what it needs to
2261
- process.removeAllListeners(signal);
2262
- process.on(signal, handler);
2263
- });
2264
- };
2265
- const createStrapiInstance = async (opts = {})=>{
2266
- try {
2267
- const appContext = await core.compileStrapi();
2268
- const app = core.createStrapi({
2269
- ...opts,
2270
- ...appContext
2271
- });
2272
- app.log.level = opts.logLevel || 'error';
2273
- return await app.load();
2274
- } catch (error) {
2275
- if (error instanceof Error && 'code' in error && error.code === 'ECONNREFUSED') {
2276
- throw new Error('Process failed. Check the database connection with your Strapi project.');
2277
- }
2278
- throw error;
2279
- }
2280
- };
2281
- const transferDataTypes = Object.keys(dataTransfer.engine.TransferGroupPresets);
2282
- const throttleOption = new commander.Option('--throttle <delay after each entity>', `Add a delay in milliseconds between each transferred entity`).argParser(parseInteger).hideHelp(); // This option is not publicly documented
2283
- const excludeOption = new commander.Option('--exclude <comma-separated data types>', `Exclude data using comma-separated types. Available types: ${transferDataTypes.join(',')}`).argParser(getParseListWithChoices(transferDataTypes, 'Invalid options for "exclude"'));
2284
- const onlyOption = new commander.Option('--only <command-separated data types>', `Include only these types of data (plus schemas). Available types: ${transferDataTypes.join(',')}`).argParser(getParseListWithChoices(transferDataTypes, 'Invalid options for "only"'));
2285
- const validateExcludeOnly = (command)=>{
2286
- const { exclude, only } = command.opts();
2287
- if (!only || !exclude) {
2288
- return;
2289
- }
2290
- const choicesInBoth = only.filter((n)=>{
2291
- return exclude.indexOf(n) !== -1;
2292
- });
2293
- if (choicesInBoth.length > 0) {
2294
- exitWith(1, `Data types may not be used in both "exclude" and "only" in the same command. Found in both: ${choicesInBoth.join(',')}`);
2295
- }
2296
- };
2297
- const errorColors = {
2298
- fatal: chalk.red,
2299
- error: chalk.red,
2300
- silly: chalk.yellow
2301
- };
2302
- const formatDiagnostic = (operation, info)=>{
2303
- // Create log file for all incoming diagnostics
2304
- let logger$1;
2305
- const getLogger = ()=>{
2306
- if (!logger$1) {
2307
- logger$1 = logger.createLogger(logger.configs.createOutputFileConfiguration(`${operation}_${Date.now()}.log`, {
2308
- level: 'info',
2309
- format: logger.formats?.detailedLogs
2310
- }));
2311
- }
2312
- return logger$1;
2313
- };
2314
- // We don't want to write a log file until there is something to be logged
2315
- return ({ details, kind })=>{
2316
- try {
2317
- if (kind === 'error') {
2318
- const { message, severity = 'fatal' } = details;
2319
- const colorizeError = errorColors[severity];
2320
- const errorMessage = colorizeError(`[${severity.toUpperCase()}] ${message}`);
2321
- getLogger().error(errorMessage);
2322
- }
2323
- if (kind === 'info' && info) {
2324
- const { message, params, origin } = details;
2325
- const msg = `[${origin ?? 'transfer'}] ${message}\n${params ? JSON.stringify(params, null, 2) : ''}`;
2326
- getLogger().info(msg);
2327
- }
2328
- if (kind === 'warning') {
2329
- const { origin, message } = details;
2330
- getLogger().warn(`(${origin ?? 'transfer'}) ${message}`);
2331
- }
2332
- } catch (err) {
2333
- getLogger().error(err);
2334
- }
2335
- };
2336
- };
2337
- const loadersFactory = (defaultLoaders = {})=>{
2338
- const loaders = defaultLoaders;
2339
- const updateLoader = (stage, data)=>{
2340
- if (!(stage in loaders)) {
2341
- createLoader(stage);
2342
- }
2343
- const stageData = data[stage];
2344
- const elapsedTime = stageData?.startTime ? (stageData?.endTime || Date.now()) - stageData.startTime : 0;
2345
- const size = `size: ${readableBytes(stageData?.bytes ?? 0)}`;
2346
- const elapsed = `elapsed: ${elapsedTime} ms`;
2347
- const speed = elapsedTime > 0 ? `(${readableBytes((stageData?.bytes ?? 0) * 1000 / elapsedTime)}/s)` : '';
2348
- loaders[stage].text = `${stage}: ${stageData?.count ?? 0} transfered (${size}) (${elapsed}) ${!stageData?.endTime ? speed : ''}`;
2349
- return loaders[stage];
2350
- };
2351
- const createLoader = (stage)=>{
2352
- Object.assign(loaders, {
2353
- [stage]: ora()
2354
- });
2355
- return loaders[stage];
2356
- };
2357
- const getLoader = (stage)=>{
2358
- return loaders[stage];
2359
- };
2360
- return {
2361
- updateLoader,
2362
- createLoader,
2363
- getLoader
2364
- };
2365
- };
2366
- /**
2367
- * Get the telemetry data to be sent for a didDEITSProcess* event from an initialized transfer engine object
2368
- */ const getTransferTelemetryPayload = (engine)=>{
2369
- return {
2370
- eventProperties: {
2371
- source: engine?.sourceProvider?.name,
2372
- destination: engine?.destinationProvider?.name
2373
- }
2374
- };
2375
- };
2376
- /**
2377
- * Get a transfer engine schema diff handler that confirms with the user before bypassing a schema check
2378
- */ const getDiffHandler = (engine, { force, action })=>{
2379
- return async (context, next)=>{
2380
- // if we abort here, we need to actually exit the process because of conflict with inquirer prompt
2381
- setSignalHandler(async ()=>{
2382
- await abortTransfer({
2383
- engine,
2384
- strapi: strapi
2385
- });
2386
- exitWith(1, exitMessageText(action, true));
2387
- });
2388
- let workflowsStatus;
2389
- const source = 'Schema Integrity';
2390
- Object.entries(context.diffs).forEach(([uid, diffs])=>{
2391
- for (const diff of diffs){
2392
- const path = [
2393
- uid
2394
- ].concat(diff.path).join('.');
2395
- const endPath = diff.path[diff.path.length - 1];
2396
- // Catch known features
2397
- if (uid === 'plugin::review-workflows.workflow' || uid === 'plugin::review-workflows.workflow-stage' || endPath?.startsWith('strapi_stage') || endPath?.startsWith('strapi_assignee')) {
2398
- workflowsStatus = diff.kind;
2399
- } else if (diff.kind === 'added') {
2400
- engine.reportWarning(chalk.red(`${chalk.bold(path)} does not exist on source`), source);
2401
- } else if (diff.kind === 'deleted') {
2402
- engine.reportWarning(chalk.red(`${chalk.bold(path)} does not exist on destination`), source);
2403
- } else if (diff.kind === 'modified') {
2404
- engine.reportWarning(chalk.red(`${chalk.bold(path)} has a different data type`), source);
2405
- }
2406
- }
2407
- });
2408
- // output the known feature warnings
2409
- if (workflowsStatus === 'added') {
2410
- engine.reportWarning(chalk.red(`Review workflows feature does not exist on source`), source);
2411
- } else if (workflowsStatus === 'deleted') {
2412
- engine.reportWarning(chalk.red(`Review workflows feature does not exist on destination`), source);
2413
- } else if (workflowsStatus === 'modified') {
2414
- engine.panic(new TransferEngineInitializationError('Unresolved differences in schema [review workflows]'));
2415
- }
2416
- const confirmed = await confirmMessage('There are differences in schema between the source and destination, and the data listed above will be lost. Are you sure you want to continue?', {
2417
- force
2418
- });
2419
- // reset handler back to normal
2420
- setSignalHandler(()=>abortTransfer({
2421
- engine,
2422
- strapi: strapi
2423
- }));
2424
- if (confirmed) {
2425
- context.ignoredDiffs = fp.merge(context.diffs, context.ignoredDiffs);
2426
- }
2427
- return next(context);
2428
- };
2429
- };
2430
- const getAssetsBackupHandler = (engine, { force, action })=>{
2431
- return async (context, next)=>{
2432
- // if we abort here, we need to actually exit the process because of conflict with inquirer prompt
2433
- setSignalHandler(async ()=>{
2434
- await abortTransfer({
2435
- engine,
2436
- strapi: strapi
2437
- });
2438
- exitWith(1, exitMessageText(action, true));
2439
- });
2440
- console.warn('The backup for the assets could not be created inside the public directory. Ensure Strapi has write permissions on the public directory.');
2441
- const confirmed = await confirmMessage('Do you want to continue without backing up your public/uploads files?', {
2442
- force
2443
- });
2444
- if (confirmed) {
2445
- context.ignore = true;
2446
- }
2447
- // reset handler back to normal
2448
- setSignalHandler(()=>abortTransfer({
2449
- engine,
2450
- strapi: strapi
2451
- }));
2452
- return next(context);
2453
- };
2454
- };
2455
- const shouldSkipStage = (opts, dataKind)=>{
2456
- if (opts.exclude?.includes(dataKind)) {
2457
- return true;
2458
- }
2459
- if (opts.only) {
2460
- return !opts.only.includes(dataKind);
2461
- }
2462
- return false;
2463
- };
2464
- // Based on exclude/only from options, create the restore object to match
2465
- const parseRestoreFromOptions = (opts)=>{
2466
- const entitiesOptions = {
2467
- exclude: DEFAULT_IGNORED_CONTENT_TYPES,
2468
- include: undefined
2469
- };
2470
- // if content is not included, send an empty array for include
2471
- if (opts.only && !opts.only.includes('content') || opts.exclude?.includes('content')) {
2472
- entitiesOptions.include = [];
2473
- }
2474
- const restoreConfig = {
2475
- entities: entitiesOptions,
2476
- assets: !shouldSkipStage(opts, 'files'),
2477
- configuration: {
2478
- webhook: !shouldSkipStage(opts, 'config'),
2479
- coreStore: !shouldSkipStage(opts, 'config')
2480
- }
2481
- };
2482
- return restoreConfig;
2483
- };
2484
-
2485
- const { providers: { createLocalFileDestinationProvider } } = dataTransfer.file;
2486
- const { providers: { createLocalStrapiSourceProvider: createLocalStrapiSourceProvider$1 } } = dataTransfer.strapi;
2487
- const BYTES_IN_MB = 1024 * 1024;
2488
- /**
2489
- * Export command.
2490
- *
2491
- * It transfers data from a local Strapi instance to a file
2492
- *
2493
- * @param {ExportCommandOptions} opts
2494
- */ var action$2 = (async (opts)=>{
2495
- // Validate inputs from Commander
2496
- if (!fp.isObject(opts)) {
2497
- exitWith(1, 'Could not parse command arguments');
2498
- }
2499
- const strapi = await createStrapiInstance();
2500
- const source = createSourceProvider(strapi);
2501
- const destination = createDestinationProvider(opts);
2502
- const engine = dataTransfer.engine.createTransferEngine(source, destination, {
2503
- versionStrategy: 'ignore',
2504
- schemaStrategy: 'ignore',
2505
- exclude: opts.exclude,
2506
- only: opts.only,
2507
- throttle: opts.throttle,
2508
- transforms: {
2509
- links: [
2510
- {
2511
- filter (link) {
2512
- return !DEFAULT_IGNORED_CONTENT_TYPES.includes(link.left.type) && !DEFAULT_IGNORED_CONTENT_TYPES.includes(link.right.type);
2513
- }
2514
- }
2515
- ],
2516
- entities: [
2517
- {
2518
- filter (entity) {
2519
- return !DEFAULT_IGNORED_CONTENT_TYPES.includes(entity.type);
2520
- }
2521
- }
2522
- ]
2523
- }
2524
- });
2525
- engine.diagnostics.onDiagnostic(formatDiagnostic('export', opts.verbose));
2526
- const progress = engine.progress.stream;
2527
- const { updateLoader } = loadersFactory();
2528
- progress.on(`stage::start`, ({ stage, data })=>{
2529
- updateLoader(stage, data).start();
2530
- });
2531
- progress.on('stage::finish', ({ stage, data })=>{
2532
- updateLoader(stage, data).succeed();
2533
- });
2534
- progress.on('stage::progress', ({ stage, data })=>{
2535
- updateLoader(stage, data);
2536
- });
2537
- progress.on('transfer::start', async ()=>{
2538
- console.log(`Starting export...`);
2539
- await strapi.telemetry.send('didDEITSProcessStart', getTransferTelemetryPayload(engine));
2540
- });
2541
- let results;
2542
- let outFile;
2543
- try {
2544
- // Abort transfer if user interrupts process
2545
- setSignalHandler(()=>abortTransfer({
2546
- engine,
2547
- strapi
2548
- }));
2549
- results = await engine.transfer();
2550
- outFile = results.destination?.file?.path ?? '';
2551
- const outFileExists = await fse.pathExists(outFile);
2552
- if (!outFileExists) {
2553
- throw new dataTransfer.engine.errors.TransferEngineTransferError(`Export file not created "${outFile}"`);
2554
- }
2555
- // Note: we need to await telemetry or else the process ends before it is sent
2556
- await strapi.telemetry.send('didDEITSProcessFinish', getTransferTelemetryPayload(engine));
2557
- try {
2558
- const table = buildTransferTable(results.engine);
2559
- console.log(table?.toString());
2560
- } catch (e) {
2561
- console.error('There was an error displaying the results of the transfer.');
2562
- }
2563
- console.log(`Export archive is in ${chalk.green(outFile)}`);
2564
- exitWith(0, exitMessageText('export'));
2565
- } catch {
2566
- await strapi.telemetry.send('didDEITSProcessFail', getTransferTelemetryPayload(engine));
2567
- exitWith(1, exitMessageText('export', true));
2568
- }
2569
- });
2570
- /**
2571
- * It creates a local strapi destination provider
2572
- */ const createSourceProvider = (strapi)=>{
2573
- return createLocalStrapiSourceProvider$1({
2574
- async getStrapi () {
2575
- return strapi;
2576
- }
2577
- });
2578
- };
2579
- /**
2580
- * It creates a local file destination provider based on the given options
2581
- */ const createDestinationProvider = (opts)=>{
2582
- const { file, compress, encrypt, key, maxSizeJsonl } = opts;
2583
- const filepath = fp.isString(file) && file.length > 0 ? file : getDefaultExportName();
2584
- const maxSizeJsonlInMb = fp.isFinite(fp.toNumber(maxSizeJsonl)) ? fp.toNumber(maxSizeJsonl) * BYTES_IN_MB : undefined;
2585
- return createLocalFileDestinationProvider({
2586
- file: {
2587
- path: filepath,
2588
- maxSizeJsonl: maxSizeJsonlInMb
2589
- },
2590
- encryption: {
2591
- enabled: encrypt ?? false,
2592
- key: encrypt ? key : undefined
2593
- },
2594
- compression: {
2595
- enabled: compress ?? false
2596
- }
2597
- });
2598
- };
2599
-
2600
- /**
2601
- * `$ strapi export`
2602
- */ const command$2 = ()=>{
2603
- return commander.createCommand('export').description('Export data from Strapi to file').allowExcessArguments(false).addOption(new commander.Option('--no-encrypt', `Disables 'aes-128-ecb' encryption of the output file`).default(true)).addOption(new commander.Option('--no-compress', 'Disables gzip compression of output file').default(true)).addOption(new commander.Option('--verbose', 'Enable verbose logs')).addOption(new commander.Option('-k, --key <string>', 'Provide encryption key in command instead of using the prompt')).addOption(new commander.Option('-f, --file <file>', 'name to use for exported file (without extensions)')).addOption(excludeOption).addOption(onlyOption).addOption(throttleOption).hook('preAction', validateExcludeOnly).hook('preAction', promptEncryptionKey).action(action$2);
2604
- };
2605
-
2606
- const { providers: { createLocalFileSourceProvider } } = dataTransfer.file;
2607
- const { providers: { createLocalStrapiDestinationProvider: createLocalStrapiDestinationProvider$1, DEFAULT_CONFLICT_STRATEGY } } = dataTransfer.strapi;
2608
- const { createTransferEngine: createTransferEngine$1, DEFAULT_VERSION_STRATEGY, DEFAULT_SCHEMA_STRATEGY } = dataTransfer.engine;
2609
- /**
2610
- * Import command.
2611
- *
2612
- * It transfers data from a file to a local Strapi instance
2613
- */ var action$1 = (async (opts)=>{
2614
- // validate inputs from Commander
2615
- if (!fp.isObject(opts)) {
2616
- exitWith(1, 'Could not parse arguments');
2617
- }
2618
- /**
2619
- * From strapi backup file
2620
- */ const sourceOptions = getLocalFileSourceOptions(opts);
2621
- const source = createLocalFileSourceProvider(sourceOptions);
2622
- /**
2623
- * To local Strapi instance
2624
- */ const strapiInstance = await createStrapiInstance();
2625
- /**
2626
- * Configure and run the transfer engine
2627
- */ const engineOptions = {
2628
- versionStrategy: DEFAULT_VERSION_STRATEGY,
2629
- schemaStrategy: DEFAULT_SCHEMA_STRATEGY,
2630
- exclude: opts.exclude,
2631
- only: opts.only,
2632
- throttle: opts.throttle,
2633
- transforms: {
2634
- links: [
2635
- {
2636
- filter (link) {
2637
- return !DEFAULT_IGNORED_CONTENT_TYPES.includes(link.left.type) && !DEFAULT_IGNORED_CONTENT_TYPES.includes(link.right.type);
2638
- }
2639
- }
2640
- ],
2641
- entities: [
2642
- {
2643
- filter: (entity)=>!DEFAULT_IGNORED_CONTENT_TYPES.includes(entity.type)
2644
- }
2645
- ]
2646
- }
2647
- };
2648
- const destinationOptions = {
2649
- async getStrapi () {
2650
- return strapiInstance;
2651
- },
2652
- autoDestroy: false,
2653
- strategy: opts.conflictStrategy || DEFAULT_CONFLICT_STRATEGY,
2654
- restore: parseRestoreFromOptions(engineOptions)
2655
- };
2656
- const destination = createLocalStrapiDestinationProvider$1(destinationOptions);
2657
- destination.onWarning = (message)=>console.warn(`\n${chalk.yellow('warn')}: ${message}`);
2658
- const engine = createTransferEngine$1(source, destination, engineOptions);
2659
- engine.diagnostics.onDiagnostic(formatDiagnostic('import', opts.verbose));
2660
- const progress = engine.progress.stream;
2661
- const { updateLoader } = loadersFactory();
2662
- engine.onSchemaDiff(getDiffHandler(engine, {
2663
- force: opts.force,
2664
- action: 'import'
2665
- }));
2666
- progress.on(`stage::start`, ({ stage, data })=>{
2667
- updateLoader(stage, data).start();
2668
- });
2669
- progress.on('stage::finish', ({ stage, data })=>{
2670
- updateLoader(stage, data).succeed();
2671
- });
2672
- progress.on('stage::progress', ({ stage, data })=>{
2673
- updateLoader(stage, data);
2674
- });
2675
- progress.on('transfer::start', async ()=>{
2676
- console.log('Starting import...');
2677
- await strapiInstance.telemetry.send('didDEITSProcessStart', getTransferTelemetryPayload(engine));
2678
- });
2679
- let results;
2680
- try {
2681
- // Abort transfer if user interrupts process
2682
- setSignalHandler(()=>abortTransfer({
2683
- engine,
2684
- strapi: strapi
2685
- }));
2686
- results = await engine.transfer();
2687
- try {
2688
- const table = buildTransferTable(results.engine);
2689
- console.log(table?.toString());
2690
- } catch (e) {
2691
- console.error('There was an error displaying the results of the transfer.');
2692
- }
2693
- // Note: we need to await telemetry or else the process ends before it is sent
2694
- await strapiInstance.telemetry.send('didDEITSProcessFinish', getTransferTelemetryPayload(engine));
2695
- await strapiInstance.destroy();
2696
- exitWith(0, exitMessageText('import'));
2697
- } catch (e) {
2698
- await strapiInstance.telemetry.send('didDEITSProcessFail', getTransferTelemetryPayload(engine));
2699
- exitWith(1, exitMessageText('import', true));
2700
- }
2701
- });
2702
- /**
2703
- * Infer local file source provider options based on a given filename
2704
- */ const getLocalFileSourceOptions = (opts)=>{
2705
- const options = {
2706
- file: {
2707
- path: opts.file ?? ''
2708
- },
2709
- compression: {
2710
- enabled: !!opts.decompress
2711
- },
2712
- encryption: {
2713
- enabled: !!opts.decrypt,
2714
- key: opts.key
2715
- }
2716
- };
2717
- return options;
2718
- };
2719
-
2720
- /**
2721
- * `$ strapi import`
2722
- */ const command$1 = ()=>{
2723
- return commander.createCommand('import').description('Import data from file to Strapi').allowExcessArguments(false).requiredOption('-f, --file <file>', 'path and filename for the Strapi export file you want to import').addOption(new commander.Option('-k, --key <string>', 'Provide encryption key in command instead of using the prompt')).addOption(new commander.Option('--verbose', 'Enable verbose logs')).addOption(forceOption).addOption(excludeOption).addOption(onlyOption).addOption(throttleOption).hook('preAction', validateExcludeOnly).hook('preAction', async (thisCommand)=>{
2724
- const opts = thisCommand.opts();
2725
- const ext = path.extname(String(opts.file));
2726
- // check extension to guess if we should prompt for key
2727
- if (ext === '.enc') {
2728
- if (!opts.key) {
2729
- const answers = await inquirer.prompt([
2730
- {
2731
- type: 'password',
2732
- message: 'Please enter your decryption key',
2733
- name: 'key'
2734
- }
2735
- ]);
2736
- if (!answers.key?.length) {
2737
- exitWith(1, 'No key entered, aborting import.');
2738
- }
2739
- opts.key = answers.key;
2740
- }
2741
- }
2742
- })// set decrypt and decompress options based on filename
2743
- .hook('preAction', (thisCommand)=>{
2744
- const opts = thisCommand.opts();
2745
- const { extname, parse } = path;
2746
- let file = opts.file;
2747
- if (extname(file) === '.enc') {
2748
- file = parse(file).name; // trim the .enc extension
2749
- thisCommand.opts().decrypt = true;
2750
- } else {
2751
- thisCommand.opts().decrypt = false;
2752
- }
2753
- if (extname(file) === '.gz') {
2754
- file = parse(file).name; // trim the .gz extension
2755
- thisCommand.opts().decompress = true;
2756
- } else {
2757
- thisCommand.opts().decompress = false;
2758
- }
2759
- if (extname(file) !== '.tar') {
2760
- exitWith(1, `The file '${opts.file}' does not appear to be a valid Strapi data file. It must have an extension ending in .tar[.gz][.enc]`);
2761
- }
2762
- }).hook('preAction', getCommanderConfirmMessage('The import will delete your existing data! Are you sure you want to proceed?', {
2763
- failMessage: 'Import process aborted'
2764
- })).action(action$1);
2765
- };
2766
-
2767
- const { createTransferEngine } = dataTransfer.engine;
2768
- const { providers: { createRemoteStrapiDestinationProvider, createLocalStrapiSourceProvider, createLocalStrapiDestinationProvider, createRemoteStrapiSourceProvider } } = dataTransfer.strapi;
2769
- /**
2770
- * Transfer command.
2771
- *
2772
- * Transfers data between local Strapi and remote Strapi instances
2773
- */ var action = (async (opts)=>{
2774
- // Validate inputs from Commander
2775
- if (!fp.isObject(opts)) {
2776
- exitWith(1, 'Could not parse command arguments');
2777
- }
2778
- if (!(opts.from || opts.to) || opts.from && opts.to) {
2779
- exitWith(1, 'Exactly one source (from) or destination (to) option must be provided');
2780
- }
2781
- const strapi = await createStrapiInstance();
2782
- let source;
2783
- let destination;
2784
- // if no URL provided, use local Strapi
2785
- if (!opts.from) {
2786
- source = createLocalStrapiSourceProvider({
2787
- getStrapi: ()=>strapi
2788
- });
2789
- } else {
2790
- if (!opts.fromToken) {
2791
- exitWith(1, 'Missing token for remote destination');
2792
- }
2793
- source = createRemoteStrapiSourceProvider({
2794
- getStrapi: ()=>strapi,
2795
- url: opts.from,
2796
- auth: {
2797
- type: 'token',
2798
- token: opts.fromToken
2799
- }
2800
- });
2801
- }
2802
- // if no URL provided, use local Strapi
2803
- if (!opts.to) {
2804
- destination = createLocalStrapiDestinationProvider({
2805
- getStrapi: ()=>strapi,
2806
- strategy: 'restore',
2807
- restore: parseRestoreFromOptions(opts)
2808
- });
2809
- } else {
2810
- if (!opts.toToken) {
2811
- exitWith(1, 'Missing token for remote destination');
2812
- }
2813
- destination = createRemoteStrapiDestinationProvider({
2814
- url: opts.to,
2815
- auth: {
2816
- type: 'token',
2817
- token: opts.toToken
2818
- },
2819
- strategy: 'restore',
2820
- restore: parseRestoreFromOptions(opts)
2821
- });
2822
- }
2823
- if (!source || !destination) {
2824
- exitWith(1, 'Could not create providers');
2825
- }
2826
- const engine = createTransferEngine(source, destination, {
2827
- versionStrategy: 'exact',
2828
- schemaStrategy: 'strict',
2829
- exclude: opts.exclude,
2830
- only: opts.only,
2831
- throttle: opts.throttle,
2832
- transforms: {
2833
- links: [
2834
- {
2835
- filter (link) {
2836
- return !DEFAULT_IGNORED_CONTENT_TYPES.includes(link.left.type) && !DEFAULT_IGNORED_CONTENT_TYPES.includes(link.right.type);
2837
- }
2838
- }
2839
- ],
2840
- entities: [
2841
- {
2842
- filter (entity) {
2843
- return !DEFAULT_IGNORED_CONTENT_TYPES.includes(entity.type);
2844
- }
2845
- }
2846
- ]
2847
- }
2848
- });
2849
- engine.diagnostics.onDiagnostic(formatDiagnostic('transfer', opts.verbose));
2850
- const progress = engine.progress.stream;
2851
- const { updateLoader } = loadersFactory();
2852
- engine.onSchemaDiff(getDiffHandler(engine, {
2853
- force: opts.force,
2854
- action: 'transfer'
2855
- }));
2856
- engine.addErrorHandler('ASSETS_DIRECTORY_ERR', getAssetsBackupHandler(engine, {
2857
- force: opts.force,
2858
- action: 'transfer'
2859
- }));
2860
- progress.on(`stage::start`, ({ stage, data })=>{
2861
- updateLoader(stage, data).start();
2862
- });
2863
- progress.on('stage::finish', ({ stage, data })=>{
2864
- updateLoader(stage, data).succeed();
2865
- });
2866
- progress.on('stage::progress', ({ stage, data })=>{
2867
- updateLoader(stage, data);
2868
- });
2869
- progress.on('stage::error', ({ stage, data })=>{
2870
- updateLoader(stage, data).fail();
2871
- });
2872
- progress.on('transfer::start', async ()=>{
2873
- console.log(`Starting transfer...`);
2874
- await strapi.telemetry.send('didDEITSProcessStart', getTransferTelemetryPayload(engine));
2875
- });
2876
- let results;
2877
- try {
2878
- // Abort transfer if user interrupts process
2879
- setSignalHandler(()=>abortTransfer({
2880
- engine,
2881
- strapi
2882
- }));
2883
- results = await engine.transfer();
2884
- // Note: we need to await telemetry or else the process ends before it is sent
2885
- await strapi.telemetry.send('didDEITSProcessFinish', getTransferTelemetryPayload(engine));
2886
- try {
2887
- const table = buildTransferTable(results.engine);
2888
- console.log(table?.toString());
2889
- } catch (e) {
2890
- console.error('There was an error displaying the results of the transfer.');
2891
- }
2892
- exitWith(0, exitMessageText('transfer'));
2893
- } catch (e) {
2894
- await strapi.telemetry.send('didDEITSProcessFail', getTransferTelemetryPayload(engine));
2895
- exitWith(1, exitMessageText('transfer', true));
2896
- }
2897
- });
2898
-
2899
- /**
2900
- * `$ strapi transfer`
2901
- */ const command = ()=>{
2902
- return commander.createCommand('transfer').description('Transfer data from one source to another').allowExcessArguments(false).addOption(new commander.Option('--from <sourceURL>', `URL of the remote Strapi instance to get data from`).argParser(parseURL)).addOption(new commander.Option('--from-token <token>', `Transfer token for the remote Strapi source`)).addOption(new commander.Option('--to <destinationURL>', `URL of the remote Strapi instance to send data to`).argParser(parseURL)).addOption(new commander.Option('--to-token <token>', `Transfer token for the remote Strapi destination`)).addOption(new commander.Option('--verbose', 'Enable verbose logs')).addOption(forceOption).addOption(excludeOption).addOption(onlyOption).addOption(throttleOption).hook('preAction', validateExcludeOnly).hook('preAction', ifOptions((opts)=>!(opts.from || opts.to) || opts.from && opts.to, async ()=>exitWith(1, 'Exactly one remote source (from) or destination (to) option must be provided')))// If --from is used, validate the URL and token
2903
- .hook('preAction', ifOptions((opts)=>opts.from, async (thisCommand)=>{
2904
- assertUrlHasProtocol(thisCommand.opts().from, [
2905
- 'https:',
2906
- 'http:'
2907
- ]);
2908
- if (!thisCommand.opts().fromToken) {
2909
- const answers = await inquirer.prompt([
2910
- {
2911
- type: 'password',
2912
- message: 'Please enter your transfer token for the remote Strapi source',
2913
- name: 'fromToken'
2914
- }
2915
- ]);
2916
- if (!answers.fromToken?.length) {
2917
- exitWith(1, 'No token provided for remote source, aborting transfer.');
2918
- }
2919
- thisCommand.opts().fromToken = answers.fromToken;
2920
- }
2921
- await getCommanderConfirmMessage('The transfer will delete all the local Strapi assets and its database. Are you sure you want to proceed?', {
2922
- failMessage: 'Transfer process aborted'
2923
- })(thisCommand);
2924
- }))// If --to is used, validate the URL, token, and confirm restore
2925
- .hook('preAction', ifOptions((opts)=>opts.to, async (thisCommand)=>{
2926
- assertUrlHasProtocol(thisCommand.opts().to, [
2927
- 'https:',
2928
- 'http:'
2929
- ]);
2930
- if (!thisCommand.opts().toToken) {
2931
- const answers = await inquirer.prompt([
2932
- {
2933
- type: 'password',
2934
- message: 'Please enter your transfer token for the remote Strapi destination',
2935
- name: 'toToken'
2936
- }
2937
- ]);
2938
- if (!answers.toToken?.length) {
2939
- exitWith(1, 'No token provided for remote destination, aborting transfer.');
2940
- }
2941
- thisCommand.opts().toToken = answers.toToken;
2942
- }
2943
- await getCommanderConfirmMessage('The transfer will delete existing data from the remote Strapi! Are you sure you want to proceed?', {
2944
- failMessage: 'Transfer process aborted'
2945
- })(thisCommand);
2946
- })).action(action);
2947
- };
2948
-
2949
- const commands = [
2950
- command$p,
2951
- command$o,
2952
- command$n,
2953
- command$m,
2954
- command$l,
2955
- command$8,
2956
- command$k,
2957
- command$j,
2958
- command$6,
2959
- command$i,
2960
- command$h,
2961
- command$g,
2962
- command$5,
2963
- command$f,
2964
- command$e,
2965
- command$4,
2966
- command$d,
2967
- command$c,
2968
- command$b,
2969
- command$a,
2970
- command$3,
2971
- command$9,
2972
- command$7,
2973
- command$2,
2974
- command$1,
2975
- command,
2976
- /**
2977
- * Cloud
2978
- */ cloudCli.buildStrapiCloudCommands
2979
- ];
2980
-
2981
- const silentSpinner = {
2982
- succeed () {
2983
- return this;
2984
- },
2985
- fail () {
2986
- return this;
2987
- },
2988
- start () {
2989
- return this;
2990
- },
2991
- text: '',
2992
- isSpinning: false
2993
- };
2994
- const silentProgressBar = {
2995
- start () {
2996
- return this;
2997
- },
2998
- stop () {
2999
- return this;
3000
- },
3001
- update () {
3002
- return this;
3003
- }
3004
- };
3005
- const createLogger = (options = {})=>{
3006
- const { silent = false, debug = false, timestamp = true } = options;
3007
- const state = {
3008
- errors: 0,
3009
- warning: 0
3010
- };
3011
- return {
3012
- get warnings () {
3013
- return state.warning;
3014
- },
3015
- get errors () {
3016
- return state.errors;
3017
- },
3018
- debug (...args) {
3019
- if (silent || !debug) {
3020
- return;
3021
- }
3022
- console.log(chalk.cyan(`[DEBUG]${timestamp ? `\t[${new Date().toISOString()}]` : ''}`), ...args);
3023
- },
3024
- info (...args) {
3025
- if (silent) {
3026
- return;
3027
- }
3028
- console.info(chalk.blue(`[INFO]${timestamp ? `\t[${new Date().toISOString()}]` : ''}`), ...args);
3029
- },
3030
- log (...args) {
3031
- if (silent) {
3032
- return;
3033
- }
3034
- console.info(chalk.blue(`${timestamp ? `\t[${new Date().toISOString()}]` : ''}`), ...args);
3035
- },
3036
- success (...args) {
3037
- if (silent) {
3038
- return;
3039
- }
3040
- console.info(chalk.green(`[SUCCESS]${timestamp ? `\t[${new Date().toISOString()}]` : ''}`), ...args);
3041
- },
3042
- warn (...args) {
3043
- state.warning += 1;
3044
- if (silent) {
3045
- return;
3046
- }
3047
- console.warn(chalk.yellow(`[WARN]${timestamp ? `\t[${new Date().toISOString()}]` : ''}`), ...args);
3048
- },
3049
- error (...args) {
3050
- state.errors += 1;
3051
- if (silent) {
3052
- return;
3053
- }
3054
- console.error(chalk.red(`[ERROR]${timestamp ? `\t[${new Date().toISOString()}]` : ''}`), ...args);
3055
- },
3056
- spinner (text) {
3057
- if (silent) {
3058
- return silentSpinner;
3059
- }
3060
- return ora(text);
3061
- },
3062
- progressBar (totalSize, text) {
3063
- if (silent) {
3064
- return silentProgressBar;
3065
- }
3066
- const progressBar = new cliProgress__namespace.SingleBar({
3067
- format: `${text ? `${text} |` : ''}${chalk.green('{bar}')}| {percentage}%`,
3068
- barCompleteChar: '\u2588',
3069
- barIncompleteChar: '\u2591',
3070
- hideCursor: true,
3071
- forceRedraw: true
3072
- });
3073
- progressBar.start(totalSize, 0);
3074
- return progressBar;
3075
- }
3076
- };
3077
- };
3078
-
3079
- /**
3080
- * @description Load a tsconfig.json file and return the parsed config.
3081
- *
3082
- * @internal
3083
- */ const loadTsConfig = ({ cwd, path, logger })=>{
3084
- const configPath = ts.findConfigFile(cwd, ts.sys.fileExists, path);
3085
- if (!configPath) {
3086
- return undefined;
3087
- }
3088
- const configFile = ts.readConfigFile(configPath, ts.sys.readFile);
3089
- const parsedConfig = ts.parseJsonConfigFileContent(configFile.config, ts.sys, cwd);
3090
- logger.debug(`Loaded user TS config:`, os$1.EOL, parsedConfig);
3091
- return {
3092
- config: parsedConfig,
3093
- path: configPath
3094
- };
3095
- };
3096
-
3097
- const createCLI = async (argv, command = new commander.Command())=>{
3098
- // Initial program setup
3099
- command.storeOptionsAsProperties(false).allowUnknownOption(true);
3100
- // Help command
3101
- command.helpOption('-h, --help', 'Display help for command');
3102
- command.addHelpCommand('help [command]', 'Display help for command');
3103
- command.version(version, '-v, --version', 'Output the version number');
3104
- const cwd = process.cwd();
3105
- const hasDebug = argv.includes('--debug');
3106
- const hasSilent = argv.includes('--silent');
3107
- const logger = createLogger({
3108
- debug: hasDebug,
3109
- silent: hasSilent,
3110
- timestamp: false
3111
- });
3112
- const tsconfig = loadTsConfig({
3113
- cwd,
3114
- path: 'tsconfig.json',
3115
- logger
3116
- });
3117
- const ctx = {
3118
- cwd,
3119
- logger,
3120
- tsconfig
3121
- };
3122
- // Load all commands
3123
- for (const commandFactory of commands){
3124
- try {
3125
- const subCommand = await commandFactory({
3126
- command,
3127
- argv,
3128
- ctx
3129
- });
3130
- // Add this command to the Commander command object
3131
- if (subCommand) {
3132
- command.addCommand(subCommand);
3133
- }
3134
- } catch (e) {
3135
- console.error(`Failed to load command`, e);
3136
- }
3137
- }
3138
- // TODO v6: remove these deprecation notices
3139
- const deprecatedCommands = [
3140
- {
3141
- name: 'plugin:init',
3142
- message: 'Please use `npx @strapi/sdk-plugin init` instead.'
3143
- },
3144
- {
3145
- name: 'plugin:verify',
3146
- message: 'After migrating your plugin to v5, use `strapi-plugin verify`'
3147
- },
3148
- {
3149
- name: 'plugin:watch',
3150
- message: 'After migrating your plugin to v5, use `strapi-plugin watch`'
3151
- },
3152
- {
3153
- name: 'plugin:watch:link',
3154
- message: 'After migrating your plugin to v5, use `strapi-plugin watch:link`'
3155
- },
3156
- {
3157
- name: 'plugin:build',
3158
- message: 'After migrating your plugin to v5, use `strapi-plugin build`'
3159
- }
3160
- ];
3161
- // Add hidden commands for deprecatedCommands that output a warning that the command has been removed.
3162
- deprecatedCommands.forEach(({ name, message })=>{
3163
- const deprecated = new commander.Command(name).command(name).description('(deprecated)').action(()=>{
3164
- console.warn(`The command ${name} has been deprecated. See the Strapi 5 migration guide for more information.`);
3165
- if (message) {
3166
- console.warn(message);
3167
- }
3168
- });
3169
- command.addCommand(deprecated, {
3170
- hidden: true
3171
- });
3172
- });
3173
- return command;
3174
- };
3175
- const runCLI = async (argv = process.argv, command = new commander.Command())=>{
3176
- const commands = await createCLI(argv, command);
3177
- await commands.parseAsync(argv);
3178
- };
3179
-
3180
- exports.createCLI = createCLI;
3181
- exports.getDocumentHTML = getDocumentHTML;
3182
- exports.isError = isError;
3183
- exports.loadFile = loadFile;
3184
- exports.runCLI = runCLI;
3185
- //# sourceMappingURL=index-BJFfd72y.js.map