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

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 +61 -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 +72 -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,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
  var providerMap_default = {
4
4
  mongodb: {
@@ -19,12 +19,7 @@ import filesToCopy from "../filesToCopy.js";
19
19
  import { SETTINGS_FILE_NAME_REGEX } from "../regexes.js";
20
20
  import getCodependenciesForFile from "./getCodependenciesForFile.js";
21
21
  import removeDeletedDependenciesFromMap from "../build/removeDeletedDependenciesFromMap.js";
22
- const nodeMajorVersion = parseInt(
23
- process?.version?.split(".")[0]?.replace("v", ""),
24
- 10
25
- );
26
- const __filename = fileURLToPath(import.meta.url);
27
- const __dirname = dirname(__filename);
22
+ import chalk from "chalk";
28
23
  const getDatabaseProcessIds = () => {
29
24
  try {
30
25
  const databaseProcessIds = [];
@@ -49,7 +44,7 @@ const getDatabaseProcessIds = () => {
49
44
  throw new Error(`[dev.getDatabaseProcessIds] ${exception.message}`);
50
45
  }
51
46
  };
52
- const handleSignalEvents = (processIds = []) => {
47
+ const handleSignalEvents = (processIds = [], nodeMajorVersion = 0, __dirname = "") => {
53
48
  try {
54
49
  const execArgv = ["--no-warnings"];
55
50
  if (nodeMajorVersion < 19) {
@@ -82,6 +77,46 @@ const handleSignalEvents = (processIds = []) => {
82
77
  throw new Error(`[dev.handleSignalEvents] ${exception.message}`);
83
78
  }
84
79
  };
80
+ const handleServerProcessMessages = () => {
81
+ try {
82
+ process.serverProcess.on("message", (message) => {
83
+ const processMessages = ["SERVER_CLOSED"];
84
+ if (!processMessages.includes(message)) {
85
+ process.loader.stable(message);
86
+ }
87
+ });
88
+ } catch (exception) {
89
+ throw new Error(`[dev.handleServerProcessMessages] ${exception.message}`);
90
+ }
91
+ };
92
+ const handleServerProcessSTDIO = () => {
93
+ try {
94
+ if (process.serverProcess) {
95
+ process.serverProcess.on("error", (error) => {
96
+ console.log(error);
97
+ });
98
+ process.serverProcess.stdout.on("data", (data) => {
99
+ const message = data.toString();
100
+ if (message && message.includes("App running at:")) {
101
+ process.loader.stable(message);
102
+ } else {
103
+ if (message && !message.includes("BUILD_ERROR")) {
104
+ console.log(message);
105
+ }
106
+ }
107
+ });
108
+ process.serverProcess.stderr.on("data", (data) => {
109
+ process.loader.stop();
110
+ CLILog(data.toString(), {
111
+ level: "danger",
112
+ docs: "https://cheatcode.co/docs/joystick"
113
+ });
114
+ });
115
+ }
116
+ } catch (exception) {
117
+ throw new Error(`[dev.handleServerProcessSTDIO] ${exception.message}`);
118
+ }
119
+ };
85
120
  const handleAddOrChangeFile = async (context = {}, path2 = "") => {
86
121
  try {
87
122
  if (context.isAddingOrChangingFile) {
@@ -187,7 +222,7 @@ const getWatchChangeContext = (event = "", path2 = "") => {
187
222
  try {
188
223
  const isHTMLUpdate = path2 === "index.html";
189
224
  const isUIPath = path2?.includes("ui/") || path2 === "index.css" || isHTMLUpdate;
190
- const isUIUpdate = process.hmrProcess.hasConnections && isUIPath || false;
225
+ const isUIUpdate = process.hmrProcess && process.hmrProcess.hasConnections && isUIPath || false;
191
226
  const isSettingsUpdate = path2?.match(SETTINGS_FILE_NAME_REGEX)?.length > 0;
192
227
  const isDirectory = fs.statSync(path2).isDirectory();
193
228
  const isFile = fs.statSync(path2).isFile();
@@ -242,14 +277,19 @@ const startFileWatcher = (options = {}) => {
242
277
  };
243
278
  const runInitialBuild = async (buildSettings = {}) => {
244
279
  try {
280
+ process.loader.text("Building app...");
245
281
  const filesToBuild = getFilesToBuild(buildSettings?.excludedPaths, "start");
246
- const fileResults = await buildFiles(
247
- filesToBuild,
248
- null,
249
- process.env.NODE_ENV
250
- );
282
+ const fileResults = await buildFiles({
283
+ files: filesToBuild,
284
+ environment: process.env.NODE_ENV
285
+ });
251
286
  const hasErrors = [...fileResults].filter((result) => !!result).map(({ success }) => success).includes(false);
287
+ if (hasErrors) {
288
+ console.log(chalk.redBright("Failed to start app. Correct the errors above and run joystick start again.\n"));
289
+ process.exit(1);
290
+ }
252
291
  } catch (exception) {
292
+ console.warn(exception);
253
293
  throw new Error(`[dev.runInitialBuild] ${exception.message}`);
254
294
  }
255
295
  };
@@ -287,21 +327,25 @@ const checkForRequiredFiles = () => {
287
327
  let error = `The following paths are missing and required in a Joystick project:
288
328
 
289
329
  `;
290
- error += `Files:
330
+ if (files?.length > 0) {
331
+ error += ` > Required Files:
291
332
 
292
333
  `;
293
- for (let i = 0; i < files?.length; i += 1) {
294
- const file = files[i];
295
- error += `${file.path}
296
-
334
+ for (let i = 0; i < files?.length; i += 1) {
335
+ const file = files[i];
336
+ error += ` /${file.path}
297
337
  `;
338
+ }
298
339
  }
299
- error += `Directories:
340
+ if (directories?.length > 0) {
341
+ error += ` > Required Directories:
300
342
 
301
343
  `;
302
- for (let i = 0; i < directories?.length; i += 1) {
303
- const file = directories[i];
304
- error += `${file.path}`;
344
+ for (let i = 0; i < directories?.length; i += 1) {
345
+ const file = directories[i];
346
+ error += ` /${file.path}
347
+ `;
348
+ }
305
349
  }
306
350
  CLILog(error, {
307
351
  level: "danger",
@@ -327,7 +371,7 @@ const warnInvalidJoystickEnvironment = () => {
327
371
  );
328
372
  process.exit(0);
329
373
  }
330
- if (process.env.NODE_ENV !== "test" && (!hasJoystickFolder || !hasTestsFolder)) {
374
+ if (process.env.NODE_ENV !== "test" && !hasJoystickFolder) {
331
375
  CLILog(
332
376
  "joystick start must be run in a directory with a .joystick folder.",
333
377
  {
@@ -355,18 +399,48 @@ const dev = async (options, { resolve, reject }) => {
355
399
  try {
356
400
  validateOptions(options);
357
401
  initProcess(options);
402
+ const nodeMajorVersion = parseInt(
403
+ process?.version?.split(".")[0]?.replace("v", ""),
404
+ 10
405
+ );
406
+ const __filename = fileURLToPath(import.meta.url);
407
+ const __dirname = dirname(__filename);
358
408
  warnInvalidJoystickEnvironment();
359
409
  checkForRequiredFiles();
360
410
  const settings = await loadSettings({
411
+ environment: options.environment
412
+ });
413
+ await startDatabases({
361
414
  environment: options.environment,
362
- process: options.process
415
+ port: options.port,
416
+ settings: settings.parsed
363
417
  });
364
- await startDatabases(settings.parsed);
365
418
  await runInitialBuild(settings?.parsed?.config?.build);
366
419
  await startFileWatcher(options);
367
- handleSignalEvents();
420
+ const serverProcess = await startApp({
421
+ nodeMajorVersion,
422
+ port: options?.port
423
+ });
424
+ if (serverProcess) {
425
+ process.serverProcess = serverProcess;
426
+ handleServerProcessSTDIO();
427
+ handleServerProcessMessages();
428
+ }
429
+ let hmrProcess;
430
+ if (options?.environment !== "test") {
431
+ hmrProcess = await startHMR({
432
+ nodeMajorVersion,
433
+ __dirname
434
+ });
435
+ }
436
+ handleSignalEvents(
437
+ [serverProcess.pid, hmrProcess.pid],
438
+ nodeMajorVersion,
439
+ __dirname
440
+ );
368
441
  resolve();
369
442
  } catch (exception) {
443
+ console.warn(exception);
370
444
  reject(`[dev] ${exception.message}`);
371
445
  }
372
446
  };
@@ -0,0 +1,5 @@
1
+ import os from "os";
2
+ var isWindows_default = os.platform() === "win32";
3
+ export {
4
+ isWindows_default as default
5
+ };
@@ -62,9 +62,7 @@ const loadSettings = (options, { resolve, reject }) => {
62
62
  warnIfSettingsNotFound(settingsPath);
63
63
  const settings = getSettings(settingsPath);
64
64
  warnIfInvalidJSONInSettings(settings);
65
- if (options?.process) {
66
- options.process.env.JOYSTICK_SETTINGS = settings;
67
- }
65
+ process.env.JOYSTICK_SETTINGS = settings;
68
66
  resolve({
69
67
  parsed: JSON.parse(settings),
70
68
  unparsed: settings
@@ -1,13 +1,51 @@
1
- const actionMethod = () => {
1
+ import child_process from "child_process";
2
+ import path from "path";
3
+ const handleStartServerProcess = (execArgv = {}, sessionsBeforeHMRUpdate = {}) => {
2
4
  try {
5
+ process.loader.text("Starting app...");
6
+ return child_process.fork(
7
+ path.resolve(".joystick/build/index.server.js"),
8
+ [],
9
+ {
10
+ execArgv,
11
+ // NOTE: Pipe stdin, stdout, and stderr. IPC establishes a message channel so we
12
+ // communicate with the child_process.
13
+ silent: true,
14
+ env: {
15
+ FORCE_COLOR: "1",
16
+ LOGS_PATH: process.env.LOGS_PATH,
17
+ NODE_ENV: process.env.NODE_ENV,
18
+ ROOT_URL: process.env.ROOT_URL,
19
+ PORT: process.env.PORT,
20
+ JOYSTICK_SETTINGS: process.env.JOYSTICK_SETTINGS,
21
+ HMR_SESSIONS: JSON.stringify(sessionsBeforeHMRUpdate)
22
+ }
23
+ }
24
+ );
3
25
  } catch (exception) {
4
- throw new Error(`[startApp.actionMethod] ${exception.message}`);
26
+ throw new Error(`[startApp.handleStartServerProcess] ${exception.message}`);
27
+ }
28
+ };
29
+ const getExecArgs = (nodeMajorVersion = 0) => {
30
+ try {
31
+ const execArgv = ["--no-warnings"];
32
+ if (nodeMajorVersion < 19) {
33
+ execArgv.push("--experimental-specifier-resolution=node");
34
+ }
35
+ if (process.env.NODE_ENV === "development" && process.env.IS_DEBUG_MODE === "true") {
36
+ execArgv.push("--inspect");
37
+ }
38
+ return execArgv;
39
+ } catch (exception) {
40
+ throw new Error(`[startApp.getExecArgs] ${exception.message}`);
5
41
  }
6
42
  };
7
43
  const validateOptions = (options) => {
8
44
  try {
9
45
  if (!options)
10
46
  throw new Error("options object is required.");
47
+ if (!options.nodeMajorVersion)
48
+ throw new Error("options.nodeMajorVersion is required.");
11
49
  if (!options.port)
12
50
  throw new Error("options.port is required.");
13
51
  } catch (exception) {
@@ -17,7 +55,9 @@ const validateOptions = (options) => {
17
55
  const startApp = (options, { resolve, reject }) => {
18
56
  try {
19
57
  validateOptions(options);
20
- resolve();
58
+ const execArgv = getExecArgs(options?.nodeMajorVersion);
59
+ const serverProcess = handleStartServerProcess(execArgv);
60
+ return resolve(serverProcess);
21
61
  } catch (exception) {
22
62
  reject(`[startApp] ${exception.message}`);
23
63
  }
@@ -1,6 +1,6 @@
1
- import util from "util";
2
1
  import CLILog from "../CLILog.js";
3
2
  import providerMap from "./databases/providerMap.js";
3
+ import { isObject } from "../types.js";
4
4
  const startDatabaseProvider = async (environment = "development", database = {}, port = 2610, hasMultipleOfProvider = false) => {
5
5
  try {
6
6
  const provider = providerMap[database?.provider];
@@ -19,12 +19,13 @@ const startDatabaseProvider = async (environment = "development", database = {},
19
19
  throw new Error(`[startDatabases.startDatabaseProvider] ${exception.message}`);
20
20
  }
21
21
  };
22
- const startDatabaseProviders = async (databases = [], databasePortStart = 2610) => {
22
+ const startDatabaseProviders = async (databases = [], databasePortStart = 2610, environment = "") => {
23
23
  try {
24
24
  for (let i = 0; i < databases?.length; i += 1) {
25
25
  const database = databases[i];
26
26
  const hasMultipleOfProvider = databases?.filter((database2) => database2?.provider === database2?.provider)?.length > 1;
27
27
  await startDatabaseProvider(
28
+ environment,
28
29
  database,
29
30
  // NOTE: Increment each database port using index in the databases array from settings if no port
30
31
  // is assigned in the settings.
@@ -39,7 +40,7 @@ const startDatabaseProviders = async (databases = [], databasePortStart = 2610)
39
40
  const validateDatabasesFromSettings = (databases = []) => {
40
41
  try {
41
42
  const databasesNotAsObjects = databases.filter(
42
- (database) => !util.types.isKeyObject(database)
43
+ (database) => !isObject(database)
43
44
  );
44
45
  const userDatabases = databases.filter((database) => !!database.users);
45
46
  const queueDatabases = databases.filter((database) => !!database.queues);
@@ -73,6 +74,8 @@ const validateOptions = (options) => {
73
74
  try {
74
75
  if (!options)
75
76
  throw new Error("options object is required.");
77
+ if (!options.environment)
78
+ throw new Error("options.environment is required.");
76
79
  if (!options.port)
77
80
  throw new Error("options.port is required.");
78
81
  if (!options.settings)
@@ -84,10 +87,14 @@ const validateOptions = (options) => {
84
87
  const startDatabases = async (options, { resolve, reject }) => {
85
88
  try {
86
89
  validateOptions(options);
87
- const databases = options?.settings?.databases || [];
90
+ const databases = options?.settings?.config?.databases || [];
88
91
  if (databases?.length > 0) {
89
92
  validateDatabasesFromSettings(databases);
90
- await startDatabaseProviders(databases, options?.port);
93
+ await startDatabaseProviders(
94
+ databases,
95
+ options?.port + 10,
96
+ options?.environment
97
+ );
91
98
  }
92
99
  resolve();
93
100
  } catch (exception) {
@@ -1,15 +1,38 @@
1
- const actionMethod = () => {
1
+ import child_process from "child_process";
2
+ import path from "path";
3
+ const handleStartHMRProcess = (execArgv = {}, __dirname = "") => {
2
4
  try {
5
+ return child_process.fork(
6
+ path.resolve(`${__dirname}/hmrServer.js`),
7
+ [],
8
+ {
9
+ execArgv,
10
+ // NOTE: Pipe stdin, stdout, and stderr. IPC establishes a message channel so we
11
+ // communicate with the child_process.
12
+ silent: true
13
+ }
14
+ );
3
15
  } catch (exception) {
4
- throw new Error(`[startHMR.actionMethod] ${exception.message}`);
16
+ throw new Error(`[startHMR.handleStartHMRProcess] ${exception.message}`);
17
+ }
18
+ };
19
+ const getExecArgs = (nodeMajorVersion = 0) => {
20
+ try {
21
+ const execArgv = ["--no-warnings"];
22
+ if (nodeMajorVersion < 19) {
23
+ execArgv.push("--experimental-specifier-resolution=node");
24
+ }
25
+ return execArgv;
26
+ } catch (exception) {
27
+ throw new Error(`[startHMR.getExecArgs] ${exception.message}`);
5
28
  }
6
29
  };
7
30
  const validateOptions = (options) => {
8
31
  try {
9
32
  if (!options)
10
33
  throw new Error("options object is required.");
11
- if (!options.port)
12
- throw new Error("options.port is required.");
34
+ if (!options.nodeMajorVersion)
35
+ throw new Error("options.nodeMajorVersion is required.");
13
36
  } catch (exception) {
14
37
  throw new Error(`[startHMR.validateOptions] ${exception.message}`);
15
38
  }
@@ -17,7 +40,9 @@ const validateOptions = (options) => {
17
40
  const startHMR = (options, { resolve, reject }) => {
18
41
  try {
19
42
  validateOptions(options);
20
- resolve();
43
+ const execArgv = getExecArgs(options?.nodeMajorVersion);
44
+ const hmrProcess = handleStartHMRProcess(execArgv, options?.__dirname);
45
+ return resolve(hmrProcess);
21
46
  } catch (exception) {
22
47
  reject(`[startHMR] ${exception.message}`);
23
48
  }
@@ -0,0 +1,60 @@
1
+ import condense from "selective-whitespace";
2
+ import netstats from "netstats";
3
+ const platform = process.platform;
4
+ function pushTo(target, item) {
5
+ if (item !== "" && typeof item === "number" && item !== 0 && target.indexOf(item) === -1) {
6
+ target.push(item);
7
+ }
8
+ }
9
+ async function processNetStats(arr) {
10
+ const pidindex = 1;
11
+ let items = arr.slice(1);
12
+ if (platform === "win32") {
13
+ items = arr;
14
+ }
15
+ const pids = {
16
+ all: [],
17
+ tcp: [],
18
+ udp: []
19
+ };
20
+ await Promise.all(items.map((item) => {
21
+ const values = condense(item).split(" ");
22
+ let pid = parseInt(values[pidindex], 10);
23
+ if (platform === "win32") {
24
+ pid = parseInt(values.pop(), 10);
25
+ }
26
+ if (values.length > 1) {
27
+ if (values.indexOf("TCP") !== -1) {
28
+ pushTo(pids.tcp, pid);
29
+ pushTo(pids.all, pid);
30
+ } else if (values.indexOf("UDP") !== -1) {
31
+ pushTo(pids.udp, pid);
32
+ pushTo(pids.all, pid);
33
+ }
34
+ }
35
+ return Promise.resolve();
36
+ }));
37
+ return pids;
38
+ }
39
+ function getProcessIdFromPort(port) {
40
+ if (typeof port !== "number") {
41
+ throw new TypeError("Expected a port number");
42
+ }
43
+ return new Promise((resolve) => {
44
+ netstats(port).then((stats) => {
45
+ processNetStats(stats).then((ps) => {
46
+ resolve(ps);
47
+ });
48
+ }).catch(() => {
49
+ resolve({
50
+ all: [],
51
+ tcp: [],
52
+ udp: []
53
+ });
54
+ });
55
+ });
56
+ }
57
+ var getProcessIdFromPort_default = getProcessIdFromPort;
58
+ export {
59
+ getProcessIdFromPort_default as default
60
+ };
@@ -0,0 +1,6 @@
1
+ const isObject = (value) => {
2
+ return value !== null && !Array.isArray(value) && typeof value === "object";
3
+ };
4
+ export {
5
+ isObject
6
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@joystick.js/cli-canary",
3
- "version": "0.0.0-canary.7",
3
+ "version": "0.0.0-canary.70",
4
4
  "type": "module",
5
5
  "description": "CLI for the Joystick JavaScript framework.",
6
6
  "main": "development.js",