@joystick.js/cli-canary 0.0.0-canary.7 → 0.0.0-canary.71

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 (37) hide show
  1. package/dist/functions/start/index.js +4 -470
  2. package/dist/functions/start/onWarn.js +1 -1
  3. package/dist/lib/build/buildFiles.js +67 -5
  4. package/dist/lib/build/buildPlugins.js +18 -15
  5. package/dist/lib/build/getCodeFrame.js +3 -4
  6. package/dist/lib/build/onWarn.js +1 -1
  7. package/dist/lib/build/removeDeletedDependenciesFromMap.js +1 -1
  8. package/dist/lib/dev/databases/mongodb/index.js +1 -1
  9. package/dist/lib/dev/databases/postgresql/index.js +2 -2
  10. package/dist/lib/dev/databases/providerMap.js +1 -1
  11. package/dist/lib/dev/index.js +100 -26
  12. package/dist/lib/dev/isWindows.js +5 -0
  13. package/dist/lib/dev/loadSettings.js +1 -3
  14. package/dist/lib/dev/startApp.js +43 -3
  15. package/dist/lib/dev/startDatabases.js +12 -5
  16. package/dist/lib/dev/startHMR.js +30 -5
  17. package/dist/lib/getProcessIdFromPort.js +60 -0
  18. package/dist/lib/types.js +6 -0
  19. package/package.json +1 -1
  20. package/src/functions/start/index.js +615 -612
  21. package/src/functions/start/onWarn.js +1 -1
  22. package/src/lib/build/buildFiles.js +79 -6
  23. package/src/lib/build/buildPlugins.js +29 -22
  24. package/src/lib/build/getCodeFrame.js +3 -4
  25. package/src/lib/build/onWarn.js +1 -1
  26. package/src/lib/build/removeDeletedDependenciesFromMap.js +1 -1
  27. package/src/lib/dev/databases/mongodb/index.js +1 -1
  28. package/src/lib/dev/databases/postgresql/index.js +2 -2
  29. package/src/lib/dev/databases/providerMap.js +1 -1
  30. package/src/lib/dev/index.js +118 -33
  31. package/src/lib/dev/isWindows.js +3 -0
  32. package/src/lib/dev/loadSettings.js +1 -3
  33. package/src/lib/dev/startApp.js +52 -6
  34. package/src/lib/dev/startDatabases.js +12 -6
  35. package/src/lib/dev/startHMR.js +36 -7
  36. package/src/lib/getProcessIdFromPort.js +92 -0
  37. package/src/lib/types.js +3 -0
@@ -1,7 +1,7 @@
1
1
  import chalk from "chalk";
2
2
  import { OBJECT_REGEX } from "../../lib/regexes.js";
3
3
  import rainbowRoad from '../../lib/rainbowRoad.js';
4
- import getCodeFrame from "../../lib/getCodeFrame.js";
4
+ import getCodeFrame from "../../lib/build/getCodeFrame.js";
5
5
 
6
6
  const removeLocationDataFromStackTrace = (stackTrace = "") => {
7
7
  return stackTrace.replace(OBJECT_REGEX, "");
@@ -8,6 +8,43 @@ import browserPathExclusions from "./browserPathExclusions.js";
8
8
  import nodePaths from "./nodePaths.js";
9
9
  import nodePathExclusions from "./nodePathExclusions.js";
10
10
  import buildPlugins from "./buildPlugins.js";
11
+ import getCodeFrame from "./getCodeFrame.js";
12
+ import onWarn from "./onWarn.js";
13
+
14
+ const handleBuildException = (exception = {}, file = '') => {
15
+ try {
16
+ const error = exception?.errors && exception?.errors[0];
17
+ const snippet = fs.existsSync(file)
18
+ ? getCodeFrame(file, {
19
+ line: error?.location?.line,
20
+ column: error?.location?.column,
21
+ })
22
+ : null;
23
+
24
+ onWarn({
25
+ file,
26
+ stack: exception?.stack,
27
+ line: error?.location?.line,
28
+ column: error?.location?.column,
29
+ snippet,
30
+ lineWithError: error?.location?.lineText?.trim(),
31
+ message: error?.text,
32
+ });
33
+
34
+ return snippet;
35
+ } catch (exception) {
36
+ throw new Error(`[actionName.handleBuildException] ${exception.message}`);
37
+ }
38
+ };
39
+
40
+ const handleParseFilePathFromException = (exception = {}) => {
41
+ try {
42
+ const rawErrorMessage = exception?.message?.split(':');
43
+ return rawErrorMessage[1] && rawErrorMessage[1]?.replace('\n', '') || '';
44
+ } catch (exception) {
45
+ throw new Error(`[actionName.handleParseFilePathFromException] ${exception.message}`);
46
+ }
47
+ };
11
48
 
12
49
  const handleBuildForNode = (nodePaths = [], options = {}) => {
13
50
  try {
@@ -63,8 +100,10 @@ const handleCopyFiles = (files = [], outputPath = '') => {
63
100
  for (let i = 0; i < files?.length; i += 1) {
64
101
  const file = files[i];
65
102
  const fileContents = fs.readFileSync(file.path);
66
- // TODO: I was using fs-extra here but I don't think I need it? Was using fs.outputFileSync()...
67
- fs.writeFileSync(
103
+
104
+ // NOTE: Using fs.outputFileSync() from fs-extra to ensure parent path is created
105
+ // if it doesn't exist (avoids need for separate fs.mkdirSync()).
106
+ fs.outputFileSync(
68
107
  `${outputPath || "./.joystick/build"}/${file.path}`,
69
108
  fileContents
70
109
  );
@@ -129,6 +168,13 @@ const getFilePlatform = (path = '') => {
129
168
  const isBrowser = isBrowserPath(path);
130
169
  const isNode = isNodePath(path);
131
170
 
171
+ console.log({
172
+ path,
173
+ isCopy,
174
+ isBrowser,
175
+ isNode,
176
+ });
177
+
132
178
  if (isCopy) {
133
179
  platform = "copy";
134
180
  }
@@ -181,9 +227,36 @@ const buildFiles = async (options, { resolve, reject }) => {
181
227
 
182
228
  handleCopyFiles(copyFiles, options?.outputPath);
183
229
 
184
- await Promise.all([
185
- handleBuildForBrowser(browserFiles, options),
186
- handleBuildForNode(nodeFiles, options),
230
+ const fileResults = await Promise.all([
231
+ handleBuildForBrowser(browserFiles, options).then(() => {
232
+ return { success: true };
233
+ }).catch((exception) => {
234
+ const file = handleParseFilePathFromException(exception);
235
+ console.log(file, exception);
236
+ const snippet = handleBuildException(exception, file);
237
+
238
+ return {
239
+ success: false,
240
+ path: file,
241
+ error: {
242
+ stack: exception?.stack,
243
+ snippet,
244
+ },
245
+ };
246
+ }),
247
+ handleBuildForNode(nodeFiles, options).catch((exception) => {
248
+ const file = handleParseFilePathFromException(exception);
249
+ const snippet = handleBuildException(exception, file);
250
+
251
+ return {
252
+ success: false,
253
+ path: file,
254
+ error: {
255
+ stack: exception?.stack,
256
+ snippet,
257
+ },
258
+ };
259
+ }),
187
260
  ]);
188
261
 
189
262
  if (options?.environment !== 'development') {
@@ -192,7 +265,7 @@ const buildFiles = async (options, { resolve, reject }) => {
192
265
  }));
193
266
  }
194
267
 
195
- resolve();
268
+ resolve(fileResults);
196
269
  } catch (exception) {
197
270
  reject(`[buildFiles] ${exception.message}`);
198
271
  }
@@ -25,22 +25,25 @@ export default {
25
25
  (bootstrapTarget) => {
26
26
  return args.path.includes(bootstrapTarget);
27
27
  }
28
- );
28
+ );
29
+
29
30
  const isLayoutComponent = [getPlatformSafePath("ui/layouts")].some(
30
31
  (bootstrapTarget) => {
31
32
  return args.path.includes(bootstrapTarget);
32
33
  }
33
- );
34
+ );
35
+
34
36
  const isPageComponent = [getPlatformSafePath("ui/pages")].some(
35
37
  (bootstrapTarget) => {
36
38
  return args.path.includes(bootstrapTarget);
37
39
  }
38
- );
40
+ );
41
+
39
42
  const isEmailComponent = [getPlatformSafePath("email/")].some(
40
43
  (bootstrapTarget) => {
41
44
  return args.path.includes(bootstrapTarget);
42
45
  }
43
- );
46
+ );
44
47
 
45
48
  if (
46
49
  shouldSetSSRId ||
@@ -51,7 +54,7 @@ export default {
51
54
  const code = fs.readFileSync(
52
55
  getPlatformSafePath(args.path),
53
56
  "utf-8"
54
- );
57
+ );
55
58
 
56
59
  // NOTE: Check to see if we have a valid component file.
57
60
  const joystickUIMatches = code.match(JOYSTICK_UI_REGEX) || [];
@@ -65,8 +68,8 @@ export default {
65
68
  console.warn(
66
69
  chalk.yellowBright(
67
70
  `All Joystick components in the ui directory must have an export default statement (e.g., export default MyComponent, export default MyLayout, or export default MyPage). Please check the file at ${args.path}.`
68
- )
69
- );
71
+ )
72
+ );
70
73
  console.log(" ");
71
74
  return;
72
75
  }
@@ -110,7 +113,7 @@ export default {
110
113
 
111
114
  export default ${componentName};
112
115
  `
113
- );
116
+ );
114
117
  }
115
118
 
116
119
  if (componentName && isPageComponent) {
@@ -128,7 +131,7 @@ export default {
128
131
 
129
132
  export default ${componentName};
130
133
  `
131
- );
134
+ );
132
135
  }
133
136
 
134
137
  return {
@@ -143,25 +146,29 @@ export default {
143
146
 
144
147
  build.onEnd(() => {
145
148
  return new Promise((resolve) => {
146
- const shouldSetComponentId = [
147
- getPlatformSafePath("ui/"),
148
- getPlatformSafePath("email/"),
149
+ for (let i = 0; i < build?.initialOptions?.entryPoints?.length; i += 1) {
150
+ const entryPoint = build?.initialOptions?.entryPoints[i];
151
+ const shouldSetComponentId = [
152
+ getPlatformSafePath("ui/"),
153
+ getPlatformSafePath("email/"),
149
154
  ].some((bootstrapTarget) => {
150
- return build.initialOptions.outfile.includes(bootstrapTarget);
155
+ return entryPoint.includes(bootstrapTarget);
151
156
  });
152
157
 
153
- if (shouldSetComponentId) {
154
- const file = fs.readFileSync(build.initialOptions.outfile, "utf-8");
155
- const joystickUIMatches =
158
+ if (shouldSetComponentId) {
159
+ const file = fs.readFileSync(`${build?.initialOptions?.outdir}/${entryPoint}`, "utf-8");
160
+ const joystickUIMatches =
156
161
  file?.match(JOYSTICK_COMPONENT_REGEX) || [];
157
162
 
158
- if (joystickUIMatches?.length > 0) {
159
- let contents = setComponentId(file)?.replace(
160
- /\.component\(\/\*\*\//g,
161
- ".component("
163
+ if (joystickUIMatches?.length > 0) {
164
+ let contents = setComponentId(file)?.replace(
165
+ /\.component\(\/\*\*\//g,
166
+ ".component("
162
167
  );
163
- fs.writeFileSync(build.initialOptions.outfile, contents);
164
- }
168
+
169
+ fs.writeFileSync(`${build?.initialOptions?.outdir}/${entryPoint}`, contents);
170
+ }
171
+ }
165
172
  }
166
173
 
167
174
  resolve();
@@ -1,8 +1,7 @@
1
1
  import fs from 'fs';
2
2
  import { codeFrameColumns } from "@babel/code-frame";
3
3
 
4
- export default (id = "", location = {}) => {
5
- const file = fs.readFileSync(id, "utf-8");
6
- const frame = codeFrameColumns(file, { start: location });
7
- return frame;
4
+ export default (path = "", location = {}) => {
5
+ const file = fs.readFileSync(path, "utf-8");
6
+ return codeFrameColumns(file, { start: location });
8
7
  };
@@ -1,7 +1,7 @@
1
1
  import chalk from "chalk";
2
2
  import { OBJECT_REGEX } from "../regexes.js";
3
3
  import rainbowRoad from '../rainbowRoad.js';
4
- import getCodeFrame from "../getCodeFrame.js";
4
+ import getCodeFrame from "./getCodeFrame.js";
5
5
 
6
6
  const removeLocationDataFromStackTrace = (stackTrace = "") => {
7
7
  return stackTrace.replace(OBJECT_REGEX, "");
@@ -1,5 +1,5 @@
1
1
  import fs from 'fs';
2
- import readFileDependencyMap from "./readFileDependencyMap.js";
2
+ import readFileDependencyMap from "../dev/readFileDependencyMap.js";
3
3
 
4
4
  export default (deletedDependencies = []) => {
5
5
  const fileDependencyMap = readFileDependencyMap();
@@ -4,7 +4,7 @@ import child_process from "child_process";
4
4
  import { kill as killPortProcess } from 'cross-port-killer';
5
5
  import isWindows from "../../isWindows.js";
6
6
  import CLILog from "../../../../lib/CLILog.js";
7
- import getProcessIdFromPort from "../../getProcessIdFromPort.js";
7
+ import getProcessIdFromPort from "../../../getProcessIdFromPort.js";
8
8
 
9
9
  const getMongoProcessId = async (port = 2601) => {
10
10
  const pids = await getProcessIdFromPort(port);
@@ -4,8 +4,8 @@ import util from "util";
4
4
  import commandExists from "command-exists";
5
5
  import child_process from "child_process";
6
6
  import { kill as killPortProcess } from 'cross-port-killer';
7
- import getProcessIdFromPort from "../../getProcessIdFromPort.js";
8
- import CLILog from "../../../../lib/CLILog.js";
7
+ import getProcessIdFromPort from "../../../getProcessIdFromPort.js";
8
+ import CLILog from "../../../CLILog.js";
9
9
 
10
10
  const exec = util.promisify(child_process.exec);
11
11
 
@@ -1,4 +1,4 @@
1
- import connectMongoDB from './mongodb/connect.js.js';
1
+ import connectMongoDB from './mongodb/connect.js';
2
2
  import connectPostgreSQL from './postgresql/connect.js';
3
3
 
4
4
  export default {
@@ -21,14 +21,7 @@ import filesToCopy from "../filesToCopy.js";
21
21
  import { SETTINGS_FILE_NAME_REGEX } from "../regexes.js";
22
22
  import getCodependenciesForFile from "./getCodependenciesForFile.js";
23
23
  import removeDeletedDependenciesFromMap from "../build/removeDeletedDependenciesFromMap.js";
24
-
25
- const nodeMajorVersion = parseInt(
26
- process?.version?.split(".")[0]?.replace("v", ""),
27
- 10
28
- );
29
-
30
- const __filename = fileURLToPath(import.meta.url);
31
- const __dirname = dirname(__filename);
24
+ import chalk from "chalk";
32
25
 
33
26
  const getDatabaseProcessIds = () => {
34
27
  try {
@@ -61,7 +54,7 @@ const getDatabaseProcessIds = () => {
61
54
  }
62
55
  };
63
56
 
64
- const handleSignalEvents = (processIds = []) => {
57
+ const handleSignalEvents = (processIds = [], nodeMajorVersion = 0, __dirname = '') => {
65
58
  try {
66
59
  const execArgv = ["--no-warnings"];
67
60
 
@@ -99,6 +92,53 @@ const handleSignalEvents = (processIds = []) => {
99
92
  }
100
93
  };
101
94
 
95
+ const handleServerProcessMessages = () => {
96
+ try {
97
+ process.serverProcess.on("message", (message) => {
98
+ const processMessages = ["SERVER_CLOSED"];
99
+
100
+ if (!processMessages.includes(message)) {
101
+ process.loader.stable(message);
102
+ }
103
+ });
104
+ } catch (exception) {
105
+ throw new Error(`[dev.handleServerProcessMessages] ${exception.message}`);
106
+ }
107
+ };
108
+
109
+ const handleServerProcessSTDIO = () => {
110
+ try {
111
+ if (process.serverProcess) {
112
+ process.serverProcess.on("error", (error) => {
113
+ console.log(error);
114
+ });
115
+
116
+ process.serverProcess.stdout.on("data", (data) => {
117
+ const message = data.toString();
118
+
119
+ if (message && message.includes("App running at:")) {
120
+ process.loader.stable(message);
121
+ } else {
122
+ if (message && !message.includes("BUILD_ERROR")) {
123
+ console.log(message);
124
+ }
125
+ }
126
+ });
127
+
128
+ process.serverProcess.stderr.on("data", (data) => {
129
+ process.loader.stop();
130
+
131
+ CLILog(data.toString(), {
132
+ level: "danger",
133
+ docs: "https://cheatcode.co/docs/joystick",
134
+ });
135
+ });
136
+ }
137
+ } catch (exception) {
138
+ throw new Error(`[dev.handleServerProcessSTDIO] ${exception.message}`);
139
+ }
140
+ };
141
+
102
142
  const handleAddOrChangeFile = async (context = {}, path = '') => {
103
143
  try {
104
144
  if (context.isAddingOrChangingFile) {
@@ -228,7 +268,8 @@ const getWatchChangeContext = (event = '', path = '') => {
228
268
  try {
229
269
  const isHTMLUpdate = path === "index.html";
230
270
  const isUIPath = path?.includes("ui/") || path === 'index.css' || isHTMLUpdate;
231
- const isUIUpdate = (process.hmrProcess.hasConnections && isUIPath) || false;
271
+ // TODO: Flimsy. May want to wait until HMR and server are up to do the builds/watching.
272
+ const isUIUpdate = (process.hmrProcess && process.hmrProcess.hasConnections && isUIPath) || false;
232
273
  const isSettingsUpdate = path?.match(SETTINGS_FILE_NAME_REGEX)?.length > 0;
233
274
  const isDirectory = fs.statSync(path).isDirectory();
234
275
  const isFile = fs.statSync(path).isFile();
@@ -292,21 +333,26 @@ const startFileWatcher = (options = {}) => {
292
333
 
293
334
  const runInitialBuild = async (buildSettings = {}) => {
294
335
  try {
336
+ process.loader.text('Building app...');
337
+
295
338
  const filesToBuild = getFilesToBuild(buildSettings?.excludedPaths, "start");
296
- const fileResults = await buildFiles(
297
- filesToBuild,
298
- null,
299
- process.env.NODE_ENV
300
- );
339
+ const fileResults = await buildFiles({
340
+ files: filesToBuild,
341
+ environment: process.env.NODE_ENV
342
+ });
301
343
 
302
344
  const hasErrors = [...fileResults]
303
345
  .filter((result) => !!result)
304
346
  .map(({ success }) => success)
305
347
  .includes(false);
306
348
 
307
- // TODO: Prevent startup if this build fails (hasError > 0)
308
-
349
+ // NOTE: If the initial build fails, exit the startup process.
350
+ if (hasErrors) {
351
+ console.log(chalk.redBright('Failed to start app. Correct the errors above and run joystick start again.\n'));
352
+ process.exit(1);
353
+ }
309
354
  } catch (exception) {
355
+ console.warn(exception);
310
356
  throw new Error(`[dev.runInitialBuild] ${exception.message}`);
311
357
  }
312
358
  };
@@ -352,18 +398,22 @@ const checkForRequiredFiles = () => {
352
398
 
353
399
  let error = `The following paths are missing and required in a Joystick project:\n\n`;
354
400
 
355
- error += `Files:\n\n`;
401
+ if (files?.length > 0) {
402
+ error += ` > Required Files:\n\n`;
356
403
 
357
- for (let i = 0; i < files?.length; i += 1) {
358
- const file = files[i];
359
- error += `${file.path}\n\n`;
404
+ for (let i = 0; i < files?.length; i += 1) {
405
+ const file = files[i];
406
+ error += ` /${file.path}\n`;
407
+ }
360
408
  }
361
409
 
362
- error += `Directories:\n\n`;
410
+ if (directories?.length > 0) {
411
+ error += ` > Required Directories:\n\n`;
363
412
 
364
- for (let i = 0; i < directories?.length; i += 1) {
365
- const file = directories[i];
366
- error += `${file.path}`;
413
+ for (let i = 0; i < directories?.length; i += 1) {
414
+ const file = directories[i];
415
+ error += ` /${file.path}\n`;
416
+ }
367
417
  }
368
418
 
369
419
  CLILog(error, {
@@ -395,7 +445,7 @@ const warnInvalidJoystickEnvironment = () => {
395
445
  process.exit(0);
396
446
  }
397
447
 
398
- if (process.env.NODE_ENV !== 'test' && (!hasJoystickFolder || !hasTestsFolder)) {
448
+ if (process.env.NODE_ENV !== 'test' && !hasJoystickFolder) {
399
449
  CLILog(
400
450
  "joystick start must be run in a directory with a .joystick folder.",
401
451
  {
@@ -425,24 +475,56 @@ const dev = async (options, { resolve, reject }) => {
425
475
  validateOptions(options);
426
476
  initProcess(options);
427
477
 
478
+ const nodeMajorVersion = parseInt(
479
+ process?.version?.split(".")[0]?.replace("v", ""),
480
+ 10
481
+ );
482
+
483
+ const __filename = fileURLToPath(import.meta.url);
484
+ const __dirname = dirname(__filename);
485
+
428
486
  warnInvalidJoystickEnvironment();
429
487
  checkForRequiredFiles();
430
488
 
431
489
  const settings = await loadSettings({
432
490
  environment: options.environment,
433
- process: options.process,
434
491
  });
435
492
 
436
- await startDatabases(settings.parsed);
493
+ await startDatabases({
494
+ environment: options.environment,
495
+ port: options.port,
496
+ settings: settings.parsed
497
+ });
437
498
 
438
499
  await runInitialBuild(settings?.parsed?.config?.build);
439
500
  await startFileWatcher(options);
440
501
 
441
- handleSignalEvents();
502
+ const serverProcess = await startApp({
503
+ nodeMajorVersion,
504
+ port: options?.port,
505
+ });
442
506
 
443
- // if (options?.environment !== 'test') {
444
- // await startHMR();
445
- // }
507
+ if (serverProcess) {
508
+ process.serverProcess = serverProcess;
509
+ handleServerProcessSTDIO();
510
+ handleServerProcessMessages();
511
+ }
512
+
513
+ // NOTE: Scope this out here so we can reference the processId below.
514
+ let hmrProcess;
515
+
516
+ if (options?.environment !== 'test') {
517
+ hmrProcess = await startHMR({
518
+ nodeMajorVersion,
519
+ __dirname,
520
+ });
521
+ }
522
+
523
+ handleSignalEvents(
524
+ [serverProcess.pid, hmrProcess.pid],
525
+ nodeMajorVersion,
526
+ __dirname
527
+ );
446
528
  //
447
529
  // if (options?.environment === 'test') {
448
530
  // await runTests(options);
@@ -456,7 +538,9 @@ const dev = async (options, { resolve, reject }) => {
456
538
  - [x] If environment === 'test', check that both a .joystick fo lder exists AND a /tests folder
457
539
  exists. If no /tests, log out docs on how to scaffold your tests.
458
540
  - [x] Load settings.<environment>.json.
459
- - [ ] Start databases relative to options.environment (development|test) from settings.<environment>.json.
541
+ - [x] Start databases relative to options.environment (development|test) from settings.<environment>.json.
542
+ - [x] Run the initial build
543
+ - [x] Start the file watcher
460
544
  - [ ] Start the app as normal from the build directory.
461
545
  - [ ] If environment === 'test', run the tests.
462
546
  - [ ] If environment === 'test', after tests, run process.exit(0).
@@ -464,6 +548,7 @@ const dev = async (options, { resolve, reject }) => {
464
548
 
465
549
  resolve();
466
550
  } catch (exception) {
551
+ console.warn(exception);
467
552
  reject(`[dev] ${exception.message}`);
468
553
  }
469
554
  };
@@ -0,0 +1,3 @@
1
+ import os from "os";
2
+
3
+ export default os.platform() === "win32";
@@ -73,9 +73,7 @@ const loadSettings = (options, { resolve, reject }) => {
73
73
  const settings = getSettings(settingsPath);
74
74
  warnIfInvalidJSONInSettings(settings);
75
75
 
76
- if (options?.process) {
77
- options.process.env.JOYSTICK_SETTINGS = settings;
78
- }
76
+ process.env.JOYSTICK_SETTINGS = settings;
79
77
 
80
78
  resolve({
81
79
  parsed: JSON.parse(settings),
@@ -1,16 +1,59 @@
1
- /* eslint-disable consistent-return */
1
+ import child_process from "child_process";
2
+ import path from "path";
2
3
 
3
- const actionMethod = () => {
4
+ const handleStartServerProcess = (execArgv = {}, sessionsBeforeHMRUpdate = {}) => {
4
5
  try {
5
- // Perform a single step in your action here.
6
+ process.loader.text('Starting app...');
7
+
8
+ return child_process.fork(
9
+ path.resolve(".joystick/build/index.server.js"),
10
+ [],
11
+ {
12
+ execArgv,
13
+ // NOTE: Pipe stdin, stdout, and stderr. IPC establishes a message channel so we
14
+ // communicate with the child_process.
15
+ silent: true,
16
+ env: {
17
+ FORCE_COLOR: "1",
18
+ LOGS_PATH: process.env.LOGS_PATH,
19
+ NODE_ENV: process.env.NODE_ENV,
20
+ ROOT_URL: process.env.ROOT_URL,
21
+ PORT: process.env.PORT,
22
+ JOYSTICK_SETTINGS: process.env.JOYSTICK_SETTINGS,
23
+ HMR_SESSIONS: JSON.stringify(sessionsBeforeHMRUpdate),
24
+ },
25
+ }
26
+ );
6
27
  } catch (exception) {
7
- throw new Error(`[startApp.actionMethod] ${exception.message}`);
28
+ throw new Error(`[startApp.handleStartServerProcess] ${exception.message}`);
29
+ }
30
+ };
31
+
32
+ const getExecArgs = (nodeMajorVersion = 0) => {
33
+ try {
34
+ const execArgv = ["--no-warnings"];
35
+
36
+ if (nodeMajorVersion < 19) {
37
+ execArgv.push("--experimental-specifier-resolution=node");
38
+ }
39
+
40
+ if (
41
+ process.env.NODE_ENV === "development" &&
42
+ process.env.IS_DEBUG_MODE === "true"
43
+ ) {
44
+ execArgv.push("--inspect");
45
+ }
46
+
47
+ return execArgv;
48
+ } catch (exception) {
49
+ throw new Error(`[startApp.getExecArgs] ${exception.message}`);
8
50
  }
9
51
  };
10
52
 
11
53
  const validateOptions = (options) => {
12
54
  try {
13
55
  if (!options) throw new Error('options object is required.');
56
+ if (!options.nodeMajorVersion) throw new Error('options.nodeMajorVersion is required.');
14
57
  if (!options.port) throw new Error('options.port is required.');
15
58
  } catch (exception) {
16
59
  throw new Error(`[startApp.validateOptions] ${exception.message}`);
@@ -20,8 +63,11 @@ const validateOptions = (options) => {
20
63
  const startApp = (options, { resolve, reject }) => {
21
64
  try {
22
65
  validateOptions(options);
23
- // Call action methods in sequence here.
24
- resolve();
66
+
67
+ const execArgv = getExecArgs(options?.nodeMajorVersion);
68
+ const serverProcess = handleStartServerProcess(execArgv);
69
+
70
+ return resolve(serverProcess);
25
71
  } catch (exception) {
26
72
  reject(`[startApp] ${exception.message}`);
27
73
  }