@schalkneethling/miyagi-core 4.3.0 → 4.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/cli/run.js ADDED
@@ -0,0 +1,281 @@
1
+ import { t } from "../i18n/index.js";
2
+ import initRendering from "../init/rendering.js";
3
+ import log from "../logger.js";
4
+ import mockGenerator from "../generator/mocks.js";
5
+ import getConfig from "../config.js";
6
+ import createCli from "../init/args.js";
7
+ import {
8
+ lint,
9
+ component as createComponentViaCli,
10
+ drupalAssets,
11
+ doctor,
12
+ } from "./index.js";
13
+ import { EXIT_CODES, MiyagiError } from "../errors.js";
14
+
15
+ /**
16
+ * @returns {object}
17
+ */
18
+ function createSuccessResult() {
19
+ return {
20
+ success: true,
21
+ code: EXIT_CODES.SUCCESS,
22
+ shouldExit: true,
23
+ };
24
+ }
25
+
26
+ /**
27
+ * @param {string} message
28
+ * @param {number} [code]
29
+ * @returns {object}
30
+ */
31
+ function createFailureResult(message, code = EXIT_CODES.GENERAL_ERROR) {
32
+ log("error", message);
33
+ return {
34
+ success: false,
35
+ code,
36
+ shouldExit: true,
37
+ message,
38
+ };
39
+ }
40
+
41
+ /**
42
+ * @param {number} code
43
+ * @param {string[]} argv
44
+ * @returns {void}
45
+ */
46
+ function maybeLogDoctorHint(code, argv) {
47
+ if (code !== EXIT_CODES.CONFIG_ERROR) {
48
+ return;
49
+ }
50
+
51
+ if (argv.includes("doctor")) {
52
+ return;
53
+ }
54
+
55
+ log("info", "Run `miyagi doctor` for a setup check.");
56
+ }
57
+
58
+ /**
59
+ * @param {object} args
60
+ * @param {boolean} [isBuild]
61
+ * @param {boolean} [isComponentGenerator]
62
+ * @returns {Promise<object>}
63
+ */
64
+ async function loadCliConfig(args, isBuild = false, isComponentGenerator = false) {
65
+ global.config = await getConfig(args, isBuild, isComponentGenerator);
66
+
67
+ if (!global.config.components.folder && !global.config.docs.folder) {
68
+ return createFailureResult(
69
+ "Please specify at least either components.folder or docs.folder in your configuration file.",
70
+ EXIT_CODES.CONFIG_ERROR,
71
+ );
72
+ }
73
+
74
+ return {
75
+ success: true,
76
+ config: global.config,
77
+ };
78
+ }
79
+
80
+ /**
81
+ * @param {object} args
82
+ * @returns {void}
83
+ */
84
+ function applyCliEnv(args) {
85
+ if (args.verbose) {
86
+ process.env.VERBOSE = String(args.verbose);
87
+ }
88
+ }
89
+
90
+ /**
91
+ * @param {object} args
92
+ * @param {object} [options]
93
+ * @param {string} [options.defaultNodeEnv]
94
+ * @param {string} [options.forcedNodeEnv]
95
+ * @param {boolean} [options.isBuild]
96
+ * @param {boolean} [options.isComponentGenerator]
97
+ * @param {Function} run
98
+ * @returns {Promise<object>}
99
+ */
100
+ async function withCliConfig(args, options = {}, run) {
101
+ applyCliEnv(args);
102
+
103
+ if (options.forcedNodeEnv) {
104
+ process.env.NODE_ENV = options.forcedNodeEnv;
105
+ } else if (options.defaultNodeEnv && !process.env.NODE_ENV) {
106
+ process.env.NODE_ENV = options.defaultNodeEnv;
107
+ }
108
+
109
+ const configResult = await loadCliConfig(
110
+ args,
111
+ options.isBuild,
112
+ options.isComponentGenerator,
113
+ );
114
+ if (!configResult.success) {
115
+ return configResult;
116
+ }
117
+
118
+ return await run(configResult.config);
119
+ }
120
+
121
+ /**
122
+ * @param {object} args
123
+ * @returns {Promise<object>}
124
+ */
125
+ async function runStartCommand(args) {
126
+ return await withCliConfig(
127
+ args,
128
+ { defaultNodeEnv: "development" },
129
+ async (config) => {
130
+ log(
131
+ "info",
132
+ t("serverStarting").replace("{{node_env}}", process.env.NODE_ENV),
133
+ );
134
+
135
+ return await initRendering(config);
136
+ },
137
+ );
138
+ }
139
+
140
+ /**
141
+ * @param {object} args
142
+ * @returns {Promise<object>}
143
+ */
144
+ async function runBuildCommand(args) {
145
+ return await withCliConfig(
146
+ args,
147
+ { forcedNodeEnv: "production", isBuild: true },
148
+ async (config) => {
149
+ log("info", t("buildStarting"));
150
+ return await initRendering(config);
151
+ },
152
+ );
153
+ }
154
+
155
+ /**
156
+ * @param {object} args
157
+ * @returns {Promise<object>}
158
+ */
159
+ async function runComponentCommand(args) {
160
+ return await withCliConfig(
161
+ args,
162
+ { defaultNodeEnv: "development", isComponentGenerator: true },
163
+ async () => {
164
+ log("info", t("generator.starting"));
165
+ return await createComponentViaCli(args);
166
+ },
167
+ );
168
+ }
169
+
170
+ /**
171
+ * @param {object} args
172
+ * @returns {Promise<object>}
173
+ */
174
+ async function runMocksCommand(args) {
175
+ return await withCliConfig(
176
+ args,
177
+ { defaultNodeEnv: "development" },
178
+ async (config) => {
179
+ const result = await mockGenerator(args.component, config.files);
180
+ if (result?.message) {
181
+ log(result.message.type, result.message.text, result.message.verbose);
182
+ }
183
+
184
+ return {
185
+ success: result.success,
186
+ code: result.success ? EXIT_CODES.SUCCESS : EXIT_CODES.GENERAL_ERROR,
187
+ shouldExit: true,
188
+ message: result.message?.text,
189
+ };
190
+ },
191
+ );
192
+ }
193
+
194
+ /**
195
+ * @param {object} args
196
+ * @returns {Promise<object>}
197
+ */
198
+ async function runLintCommand(args) {
199
+ applyCliEnv(args);
200
+ return await lint(args);
201
+ }
202
+
203
+ /**
204
+ * @param {object} args
205
+ * @returns {Promise<object>}
206
+ */
207
+ async function runDrupalAssetsCommand(args) {
208
+ return await withCliConfig(
209
+ args,
210
+ { forcedNodeEnv: "development" },
211
+ async () => await drupalAssets(args),
212
+ );
213
+ }
214
+
215
+ /**
216
+ * @param {object} args
217
+ * @returns {Promise<object>}
218
+ */
219
+ async function runDoctorCommand(args) {
220
+ applyCliEnv(args);
221
+ return await doctor(args);
222
+ }
223
+
224
+ /**
225
+ * @param {Error|MiyagiError|string} error
226
+ * @param {string[]} argv
227
+ * @returns {object}
228
+ */
229
+ function normalizeCliError(error, argv) {
230
+ if (error instanceof MiyagiError) {
231
+ if (!error.logged) {
232
+ log("error", error.message);
233
+ }
234
+ maybeLogDoctorHint(error.code, argv);
235
+
236
+ return {
237
+ success: false,
238
+ code: error.code,
239
+ shouldExit: true,
240
+ message: error.message,
241
+ };
242
+ }
243
+
244
+ const message = error instanceof Error ? error.message : String(error);
245
+ log("error", message);
246
+ maybeLogDoctorHint(EXIT_CODES.GENERAL_ERROR, argv);
247
+ return {
248
+ success: false,
249
+ code: EXIT_CODES.GENERAL_ERROR,
250
+ shouldExit: true,
251
+ message,
252
+ };
253
+ }
254
+
255
+ /**
256
+ * @param {string[]} [argv]
257
+ * @returns {Promise<object>}
258
+ */
259
+ export async function runCli(argv = process.argv) {
260
+ const { cli, getResult } = createCli(
261
+ {
262
+ start: runStartCommand,
263
+ build: runBuildCommand,
264
+ new: runComponentCommand,
265
+ mocks: runMocksCommand,
266
+ lint: runLintCommand,
267
+ drupalAssets: runDrupalAssetsCommand,
268
+ doctor: runDoctorCommand,
269
+ },
270
+ argv,
271
+ );
272
+
273
+ try {
274
+ await cli.parseAsync();
275
+ const result = getResult() || createSuccessResult();
276
+ maybeLogDoctorHint(result.code, argv);
277
+ return result;
278
+ } catch (error) {
279
+ return normalizeCliError(error, argv);
280
+ }
281
+ }
package/lib/config.js CHANGED
@@ -62,6 +62,16 @@ function getCliArgs(args) {
62
62
 
63
63
  delete cliArgs._;
64
64
  delete cliArgs.$0;
65
+ delete cliArgs.component;
66
+ delete cliArgs.verbose;
67
+ delete cliArgs.skip;
68
+ delete cliArgs.only;
69
+ delete cliArgs.engine;
70
+ delete cliArgs.config;
71
+ delete cliArgs.libraries;
72
+ delete cliArgs.components;
73
+ delete cliArgs.ignorePrefixes;
74
+ delete cliArgs.dryRun;
65
75
 
66
76
  if (cliArgs.folder) {
67
77
  buildArgs.folder = cliArgs.folder;
@@ -142,7 +142,7 @@ export default {
142
142
  report: {
143
143
  enabled: true,
144
144
  onStart: true,
145
- format: "pretty",
145
+ format: "summary",
146
146
  destination: "stdout",
147
147
  useColors: true,
148
148
  },
package/lib/errors.js ADDED
@@ -0,0 +1,31 @@
1
+ /**
2
+ * CLI/process exit codes used by miyagi.
3
+ *
4
+ * Keep this list small and coarse-grained. The goal is predictable automation,
5
+ * not a unique code for every possible failure.
6
+ */
7
+ export const EXIT_CODES = Object.freeze({
8
+ SUCCESS: 0,
9
+ GENERAL_ERROR: 1,
10
+ CLI_USAGE_ERROR: 2,
11
+ CONFIG_ERROR: 3,
12
+ VALIDATION_ERROR: 4,
13
+ });
14
+
15
+ export class MiyagiError extends Error {
16
+ /**
17
+ * @param {string} message
18
+ * @param {object} [options]
19
+ * @param {number} [options.code] Exit code from `EXIT_CODES`.
20
+ * @param {boolean} [options.logged] True if this error was already sent to the logger and should not be logged again at the CLI boundary.
21
+ */
22
+ constructor(
23
+ message,
24
+ { code = EXIT_CODES.GENERAL_ERROR, logged = false } = {},
25
+ ) {
26
+ super(message);
27
+ this.name = "MiyagiError";
28
+ this.code = code;
29
+ this.logged = logged;
30
+ }
31
+ }
package/lib/index.js CHANGED
@@ -6,81 +6,11 @@
6
6
  */
7
7
 
8
8
  import { t } from "./i18n/index.js";
9
- import initRendering from "./init/rendering.js";
10
9
  import log from "./logger.js";
11
- import yargs from "./init/args.js";
12
- import mockGenerator from "./generator/mocks.js";
13
10
  import getConfig from "./config.js";
14
- import {
15
- lint,
16
- component as createComponentViaCli,
17
- drupalAssets,
18
- } from "./cli/index.js";
11
+ import { EXIT_CODES } from "./errors.js";
19
12
  import apiApp from "../api/app.js";
20
13
 
21
- /**
22
- * Checks if miyagi was started with "mocks" command
23
- * @param {object} args - the cli args
24
- * @returns {boolean} is true if the miyagi was started with "mocks"
25
- */
26
- function argsIncludeMockGenerator(args) {
27
- return args._.includes("mocks");
28
- }
29
-
30
- /**
31
- * Checks if miyagi was started with "new" command
32
- * @param {object} args - the cli args
33
- * @returns {boolean} is true if the miyagi was started with "new"
34
- */
35
- function argsIncludeComponentGenerator(args) {
36
- return args._.includes("new");
37
- }
38
-
39
- /**
40
- * Checks if miyagi was started with "build" command
41
- * @param {object} args - the cli args
42
- * @returns {boolean} is true if the miyagi was started with "new"
43
- */
44
- function argsIncludeBuild(args) {
45
- return args._.includes("build");
46
- }
47
-
48
- /**
49
- * Checks if miyagi was started with "start" command
50
- * @param {object} args - the cli args
51
- * @returns {boolean} is true if the miyagi was started with "start"
52
- */
53
- function argsIncludeServer(args) {
54
- return args._.includes("start");
55
- }
56
-
57
- /**
58
- * Checks if miyagi was started with "lint" command
59
- * @param {object} args
60
- * @returns {boolean}
61
- */
62
- function argsIncludeLint(args) {
63
- return args._.includes("lint");
64
- }
65
-
66
- /**
67
- * @param {object} args
68
- * @returns {boolean}
69
- */
70
- function argsIncludeDrupalAssets(args) {
71
- return args._.includes("drupal-assets");
72
- }
73
-
74
- /**
75
- * Runs the mock generator
76
- * @param {object} config - the user configuration object
77
- * @param {object} args - the cli args
78
- * @returns {Promise}
79
- */
80
- function runMockGenerator(config, args) {
81
- return mockGenerator(args._.slice(1)[0], config.files).catch(() => {});
82
- }
83
-
84
14
  /**
85
15
  * @param {object} config
86
16
  * @returns {Promise<object>}
@@ -105,85 +35,11 @@ export default async function Miyagi(cmd, { isBuild: isApiBuild } = {}) {
105
35
  return await initApi(global.config);
106
36
  }
107
37
 
108
- let args;
109
- let isServer;
110
- let isBuild;
111
- let isComponentGenerator;
112
- let isMockGenerator;
113
- let isLinter;
114
- let isDrupalAssets;
115
-
116
- if (cmd) {
117
- isBuild = cmd === "build";
118
- } else {
119
- args = await yargs.argv;
120
- isServer = argsIncludeServer(args);
121
- isBuild = argsIncludeBuild(args);
122
- isComponentGenerator = argsIncludeComponentGenerator(args);
123
- isMockGenerator = argsIncludeMockGenerator(args);
124
- isLinter = argsIncludeLint(args);
125
- isDrupalAssets = argsIncludeDrupalAssets(args);
126
- }
127
-
128
- if (args && args.verbose) {
129
- process.env.VERBOSE = String(args.verbose);
130
- }
131
-
132
- if (isLinter) {
133
- return lint(args);
134
- }
135
-
136
- if (isDrupalAssets) {
137
- process.env.NODE_ENV = "development";
138
- global.config = await getConfig(args);
139
- await drupalAssets(args);
140
- process.exit();
141
- }
142
-
143
- if (isBuild || isComponentGenerator || isServer || isMockGenerator) {
144
- if (isBuild) {
145
- process.env.NODE_ENV = "production";
146
- log("info", t("buildStarting"));
147
- } else {
148
- if (!process.env.NODE_ENV) {
149
- process.env.NODE_ENV = "development";
150
- }
151
-
152
- if (isComponentGenerator) {
153
- log("info", t("generator.starting"));
154
- } else if (isServer) {
155
- log(
156
- "info",
157
- t("serverStarting").replace("{{node_env}}", process.env.NODE_ENV),
158
- );
159
- }
160
- }
161
-
162
- global.config = await getConfig(args, isBuild, isComponentGenerator);
163
-
164
- if (!global.config.components.folder && !global.config.docs.folder) {
165
- log(
166
- "error",
167
- "Please specify at least either components.folder or docs.folder in your configuration file.",
168
- );
169
- process.exit(1);
170
- }
171
-
172
- if (isMockGenerator) {
173
- await runMockGenerator(global.config, args);
174
- // Force-exit in case imported user config has active resources preventing Node.js from exiting.
175
- process.exit();
176
- }
177
-
178
- if (isComponentGenerator) {
179
- await createComponentViaCli(args);
180
- // Force-exit in case imported user config has active resources preventing Node.js from exiting.
181
- process.exit();
182
- }
183
-
184
- return initRendering(global.config);
185
- }
186
-
187
38
  log("error", t("commandNotFound"));
188
- process.exit(1);
39
+ return {
40
+ success: false,
41
+ code: EXIT_CODES.CLI_USAGE_ERROR,
42
+ shouldExit: true,
43
+ message: t("commandNotFound"),
44
+ };
189
45
  }