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