@joystick.js/cli-canary 0.0.0-canary.99 → 1.0.0-beta.75

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/{_package.json → canary.json} +2 -3
  2. package/dist/functions/index.js +16 -11
  3. package/dist/functions/start/index.js +0 -19
  4. package/dist/functions/test/index.js +2 -1
  5. package/dist/lib/build/browserPathExclusions.js +2 -1
  6. package/dist/lib/build/buildFiles.js +12 -4
  7. package/dist/lib/build/buildPlugins.js +19 -5
  8. package/dist/lib/build/nodePaths.js +1 -0
  9. package/dist/lib/build/replaceExamples.js +13 -0
  10. package/dist/lib/checkIfPortOccupied.js +27 -0
  11. package/dist/lib/dev/getFilesToBuild.js +1 -1
  12. package/dist/lib/dev/hmrServer.js +66 -0
  13. package/dist/lib/dev/index.js +132 -48
  14. package/dist/lib/dev/loadSettings.js +10 -4
  15. package/dist/lib/dev/runTests.js +47 -15
  16. package/dist/lib/dev/startApp.js +6 -4
  17. package/dist/lib/dev/tests.config.js +12 -0
  18. package/dist/lib/regexes.js +2 -0
  19. package/package.json +2 -1
  20. package/src/functions/index.js +15 -10
  21. package/src/functions/start/index.js +0 -656
  22. package/src/functions/test/index.js +1 -0
  23. package/src/lib/build/browserPathExclusions.js +2 -1
  24. package/src/lib/build/buildFiles.js +13 -4
  25. package/src/lib/build/buildPlugins.js +31 -14
  26. package/src/lib/build/nodePaths.js +1 -0
  27. package/src/lib/build/replaceExamples.js +11 -0
  28. package/src/lib/build/setComponentId.js +2 -2
  29. package/src/lib/checkIfPortOccupied.js +29 -0
  30. package/src/lib/dev/getFilesToBuild.js +1 -1
  31. package/src/lib/dev/hmrServer.js +78 -0
  32. package/src/lib/dev/index.js +146 -60
  33. package/src/lib/dev/loadSettings.js +12 -4
  34. package/src/lib/dev/runTests.js +67 -18
  35. package/src/lib/dev/startApp.js +6 -4
  36. package/src/lib/dev/tests.config.js +9 -0
  37. package/src/lib/regexes.js +1 -0
@@ -19,6 +19,9 @@ import { SETTINGS_FILE_NAME_REGEX } from "../regexes.js";
19
19
  import getCodependenciesForFile from "./getCodependenciesForFile.js";
20
20
  import removeDeletedDependenciesFromMap from "../build/removeDeletedDependenciesFromMap.js";
21
21
  import chalk from "chalk";
22
+ import checkIfPortOccupied from "../checkIfPortOccupied.js";
23
+ import { killPortProcess } from "kill-port-process";
24
+ const processIds = [];
22
25
  const getDatabaseProcessIds = () => {
23
26
  try {
24
27
  const databaseProcessIds = [];
@@ -43,7 +46,7 @@ const getDatabaseProcessIds = () => {
43
46
  throw new Error(`[dev.getDatabaseProcessIds] ${exception.message}`);
44
47
  }
45
48
  };
46
- const handleSignalEvents = (processIds = [], nodeMajorVersion = 0, __dirname = "") => {
49
+ const handleSignalEvents = (processIds2 = [], nodeMajorVersion = 0, __dirname = "") => {
47
50
  try {
48
51
  const execArgv = ["--no-warnings"];
49
52
  if (nodeMajorVersion < 19) {
@@ -62,14 +65,15 @@ const handleSignalEvents = (processIds = [], nodeMajorVersion = 0, __dirname = "
62
65
  silent: true
63
66
  }
64
67
  );
68
+ process.cleanupProcess = cleanupProcess;
65
69
  process.on("SIGINT", async () => {
66
70
  const databaseProcessIds = getDatabaseProcessIds();
67
- cleanupProcess.send(JSON.stringify({ processIds: [...processIds, ...databaseProcessIds] }));
71
+ cleanupProcess.send(JSON.stringify({ processIds: [...processIds2, ...databaseProcessIds] }));
68
72
  process.exit();
69
73
  });
70
74
  process.on("SIGTERM", async () => {
71
75
  const databaseProcessIds = getDatabaseProcessIds();
72
- cleanupProcess.send(JSON.stringify({ processIds: [...processIds, ...databaseProcessIds] }));
76
+ cleanupProcess.send(JSON.stringify({ processIds: [...processIds2, ...databaseProcessIds] }));
73
77
  process.exit();
74
78
  });
75
79
  } catch (exception) {
@@ -143,7 +147,7 @@ const handleServerProcessMessages = () => {
143
147
  throw new Error(`[dev.handleServerProcessMessages] ${exception.message}`);
144
148
  }
145
149
  };
146
- const handleServerProcessSTDIO = () => {
150
+ const handleServerProcessSTDIO = (options = {}) => {
147
151
  try {
148
152
  if (process.serverProcess) {
149
153
  process.serverProcess.on("error", (error) => {
@@ -151,7 +155,7 @@ const handleServerProcessSTDIO = () => {
151
155
  });
152
156
  process.serverProcess.stdout.on("data", (data) => {
153
157
  const message = data.toString();
154
- if (message && message.includes("App running at:")) {
158
+ if (message && message.includes("App running at:") && !options?.watch) {
155
159
  process.loader.stable(message);
156
160
  } else {
157
161
  if (message && !message.includes("BUILD_ERROR")) {
@@ -171,15 +175,37 @@ const handleServerProcessSTDIO = () => {
171
175
  throw new Error(`[dev.handleServerProcessSTDIO] ${exception.message}`);
172
176
  }
173
177
  };
178
+ const handleDeletePath = (context = {}, path2 = "", options = {}) => {
179
+ try {
180
+ if (context?.isDeletingPath) {
181
+ if (context.isExistingPathInBuild) {
182
+ const pathToUnlink = `./.joystick/build/${path2}`;
183
+ const stats = fs.lstatSync(pathToUnlink);
184
+ if (stats.isDirectory()) {
185
+ fs.rmdirSync(pathToUnlink, { recursive: true });
186
+ }
187
+ if (stats.isFile()) {
188
+ fs.unlinkSync(pathToUnlink);
189
+ }
190
+ }
191
+ if (context.isUIUpdate) {
192
+ handleNotifyHMRClients(context.isHTMLUpdate);
193
+ } else {
194
+ handleRestartApplicationProcess(options);
195
+ }
196
+ }
197
+ } catch (exception) {
198
+ throw new Error(`[dev.handleDeletePath] ${exception.message}`);
199
+ }
200
+ };
174
201
  const handleAddOrChangeFile = async (context = {}, path2 = "", options = {}) => {
175
202
  try {
176
203
  if (context.isAddingOrChangingFile) {
177
204
  const codependencies = await getCodependenciesForFile(path2);
178
- const fileResults = await buildFiles(
179
- [path2, ...codependencies?.existing || []],
180
- null,
181
- process.env.NODE_ENV
182
- );
205
+ const fileResults = await buildFiles({
206
+ files: [path2, ...codependencies?.existing || []],
207
+ environment: process.env.NODE_ENV
208
+ });
183
209
  const fileResultsHaveErrors = fileResults.filter((result) => !!result).map(({ success }) => success).includes(false);
184
210
  removeDeletedDependenciesFromMap(codependencies.deleted);
185
211
  if (process.serverProcess && fileResultsHaveErrors) {
@@ -204,6 +230,7 @@ const handleAddOrChangeFile = async (context = {}, path2 = "", options = {}) =>
204
230
  }
205
231
  }
206
232
  } catch (exception) {
233
+ console.warn(exception);
207
234
  throw new Error(`[dev.handleAddOrChangeFile] ${exception.message}`);
208
235
  }
209
236
  };
@@ -237,7 +264,7 @@ const handleCopyFile = (context = {}, path2 = "", options = {}) => {
237
264
  };
238
265
  const handleCopyDirectory = (context = {}, path2 = "", options = {}) => {
239
266
  try {
240
- if (context.isFileToCopy && context.isDirectory && !context.isExistingDirectoryInBuild) {
267
+ if (context.isFileToCopy && context.isDirectory && !context.isExistingPathInBuild) {
241
268
  fs.mkdirSync(`./.joystick/build/${path2}`);
242
269
  if (context.isUIUpdate) {
243
270
  handleNotifyHMRClients(context.isHTMLUpdate);
@@ -252,17 +279,11 @@ const handleCopyDirectory = (context = {}, path2 = "", options = {}) => {
252
279
  const handleRestartApplicationProcess = async (options = {}) => {
253
280
  try {
254
281
  if (process.serverProcess && process.serverProcess.pid) {
255
- process.loader.text("Restarting app...");
256
282
  process.serverProcess.kill();
257
- await startApp({
258
- nodeMajorVersion: options?.nodeMajorVersion,
259
- port: options?.port,
260
- sessionsBeforeHMRUpdate: options?.sessionsBeforeHMRUpdate
261
- });
283
+ await handleStartAppServer(options);
262
284
  return Promise.resolve();
263
285
  }
264
- process.loader.text("Starting app...");
265
- startApplicationProcess();
286
+ await handleStartAppServer(options);
266
287
  if (!process.hmrProcess) {
267
288
  startHMR();
268
289
  }
@@ -270,19 +291,43 @@ const handleRestartApplicationProcess = async (options = {}) => {
270
291
  throw new Error(`[dev.handleRestartApplicationProcess] ${exception.message}`);
271
292
  }
272
293
  };
273
- const handleNotifyHMRClients = (indexHTMLChanged = false) => {
294
+ const handleStartAppServer = async (options = {}) => {
295
+ try {
296
+ const serverProcess = await startApp({
297
+ watch: options?.watch,
298
+ nodeMajorVersion: options?.nodeMajorVersion,
299
+ port: options?.port,
300
+ sessionsBeforeHMRUpdate: options?.sessionsBeforeHMRUpdate
301
+ });
302
+ if (serverProcess) {
303
+ processIds.push(serverProcess.pid);
304
+ process.serverProcess = serverProcess;
305
+ handleServerProcessSTDIO(options);
306
+ handleServerProcessMessages(options);
307
+ }
308
+ } catch (exception) {
309
+ console.warn(exception);
310
+ throw new Error(`[dev.handleStartAppServer] ${exception.message}`);
311
+ }
312
+ };
313
+ const handleNotifyHMRClients = async (indexHTMLChanged = false) => {
274
314
  try {
275
315
  if (process.hmrProcess) {
276
- const settings = loadSettings(process.env.NODE_ENV);
316
+ const databaseProcessIds = getDatabaseProcessIds();
317
+ const settings = await loadSettings({
318
+ environment: process.env.NODE_ENV,
319
+ processIds: [...processIds, ...databaseProcessIds]
320
+ });
277
321
  process.hmrProcess.send(
278
322
  JSON.stringify({
279
323
  type: "RESTART_SERVER",
280
- settings,
324
+ settings: settings.parsed,
281
325
  indexHTMLChanged
282
326
  })
283
327
  );
284
328
  }
285
329
  } catch (exception) {
330
+ console.warn(exception);
286
331
  throw new Error(`[dev.handleNotifyHMRClients] ${exception.message}`);
287
332
  }
288
333
  };
@@ -292,27 +337,31 @@ const getWatchChangeContext = (event = "", path2 = "") => {
292
337
  const isUIPath = path2?.includes("ui/") || path2 === "index.css" || isHTMLUpdate;
293
338
  const isUIUpdate = process.hmrProcess && process.hmrProcess.hasConnections && isUIPath || false;
294
339
  const isSettingsUpdate = path2?.match(SETTINGS_FILE_NAME_REGEX)?.length > 0;
295
- const isDirectory = fs.statSync(path2).isDirectory();
296
- const isFile = fs.statSync(path2).isFile();
297
- const isExistingDirectoryInSource = isDirectory && fs.existsSync(path2);
298
- const isExistingDirectoryInBuild = !!fs.existsSync(`./.joystick/build/${path2}`);
299
- const isAddDirectory = event === "addDir" && isExistingDirectoryInSource && !isExistingDirectoryInBuild;
340
+ const pathExists = fs.existsSync(path2);
341
+ const isDirectory = pathExists && fs.statSync(path2).isDirectory();
342
+ const isFile = pathExists && fs.statSync(path2).isFile();
343
+ const isExistingPathInSource = isDirectory && pathExists;
344
+ const isExistingPathInBuild = !!fs.existsSync(`./.joystick/build/${path2}`);
345
+ const isAddDirectory = event === "addDir" && isExistingPathInSource && !isExistingPathInBuild;
300
346
  const isFileToCopy = !!filesToCopy.find((fileToCopy) => fileToCopy.path === path2);
301
- const isExistingFileInSource = isFile && fs.existsSync(path2);
302
- const isAddingOrChangingFile = event === "add" && isExistingFileInSource;
347
+ const isExistingFileInSource = isFile && pathExists;
348
+ const isAddingOrChangingFile = ["add", "change"].includes(event) && isExistingFileInSource;
349
+ const isDeletingPath = ["unlink", "unlinkDir"].includes(event);
303
350
  return {
351
+ pathExists,
304
352
  isHTMLUpdate,
305
353
  isUIPath,
306
354
  isUIUpdate,
307
355
  isSettingsUpdate,
308
356
  isDirectory,
309
357
  isFile,
310
- isExistingDirectoryInSource,
311
- isExistingDirectoryInBuild,
358
+ isExistingPathInSource,
359
+ isExistingPathInBuild,
312
360
  isAddDirectory,
313
361
  isFileToCopy,
314
362
  isExistingFileInSource,
315
- isAddingOrChangingFile
363
+ isAddingOrChangingFile,
364
+ isDeletingPath
316
365
  };
317
366
  } catch (exception) {
318
367
  throw new Error(`[dev.getWatchChangeContext] ${exception.message}`);
@@ -326,16 +375,20 @@ const startFileWatcher = (options = {}) => {
326
375
  );
327
376
  watcher.on("all", async (event, path2) => {
328
377
  checkForRequiredFiles();
329
- process.loader.text("Rebuilding app...");
378
+ if (!options?.watch) {
379
+ process.loader.text("Rebuilding app...");
380
+ }
330
381
  const watchChangeContext = getWatchChangeContext(event, path2);
331
382
  handleCopyDirectory(watchChangeContext, path2, options);
332
383
  handleCopyFile(watchChangeContext, path2, options);
333
384
  handleAddDirectory(watchChangeContext, path2, options);
334
385
  await handleAddOrChangeFile(watchChangeContext, path2, options);
386
+ await handleDeletePath(watchChangeContext, path2, options);
335
387
  if (watchChangeContext?.isSettingsUpdate) {
388
+ const databaseProcessIds = getDatabaseProcessIds();
336
389
  await loadSettings({
337
390
  environment: options.environment,
338
- process: options.process
391
+ processIds: [...processIds, ...databaseProcessIds]
339
392
  });
340
393
  }
341
394
  });
@@ -453,6 +506,16 @@ const warnInvalidJoystickEnvironment = () => {
453
506
  throw new Error(`[dev.warnInvalidJoystickEnvironment] ${exception.message}`);
454
507
  }
455
508
  };
509
+ const handleCleanup = () => {
510
+ try {
511
+ if (process.cleanupProcess) {
512
+ const databaseProcessIds = getDatabaseProcessIds();
513
+ process.cleanupProcess.send(JSON.stringify({ processIds: [...processIds, ...databaseProcessIds] }));
514
+ }
515
+ } catch (exception) {
516
+ throw new Error(`[actionName.handleCleanup] ${exception.message}`);
517
+ }
518
+ };
456
519
  const validateOptions = (options) => {
457
520
  try {
458
521
  if (!options)
@@ -466,6 +529,18 @@ const validateOptions = (options) => {
466
529
  const dev = async (options, { resolve, reject }) => {
467
530
  try {
468
531
  validateOptions(options);
532
+ const port = parseInt(options?.port || 2600, 10);
533
+ const appPortOccupied = await checkIfPortOccupied(port);
534
+ const hmrPortOccupied = await checkIfPortOccupied(port + 1);
535
+ if (appPortOccupied) {
536
+ CLILog(`Port ${options?.port} is already occupied. To start Joystick on this port, clear it and try again.`, {
537
+ level: "danger"
538
+ });
539
+ process.exit(0);
540
+ }
541
+ if (hmrPortOccupied) {
542
+ await killPortProcess(port + 1);
543
+ }
469
544
  initProcess(options);
470
545
  const nodeMajorVersion = parseInt(
471
546
  process?.version?.split(".")[0]?.replace("v", ""),
@@ -484,18 +559,14 @@ const dev = async (options, { resolve, reject }) => {
484
559
  settings: settings.parsed
485
560
  });
486
561
  await runInitialBuild(settings?.parsed?.config?.build);
487
- await startFileWatcher(options);
488
- const processIds = [];
489
- const serverProcess = await startApp({
490
- nodeMajorVersion,
491
- port: options?.port
562
+ await startFileWatcher({
563
+ ...options,
564
+ nodeMajorVersion
565
+ });
566
+ await handleStartAppServer({
567
+ ...options,
568
+ nodeMajorVersion
492
569
  });
493
- if (serverProcess) {
494
- processIds.push(serverProcess.pid);
495
- process.serverProcess = serverProcess;
496
- handleServerProcessSTDIO();
497
- handleServerProcessMessages();
498
- }
499
570
  if (options?.environment !== "test") {
500
571
  const hmrProcess = await startHMR({
501
572
  nodeMajorVersion,
@@ -504,7 +575,10 @@ const dev = async (options, { resolve, reject }) => {
504
575
  processIds.push(hmrProcess.pid);
505
576
  process.hmrProcess = hmrProcess;
506
577
  handleHMRProcessSTDIO();
507
- handleHMRProcessMessages(options);
578
+ handleHMRProcessMessages({
579
+ ...options,
580
+ nodeMajorVersion
581
+ });
508
582
  }
509
583
  handleSignalEvents(
510
584
  processIds,
@@ -512,10 +586,20 @@ const dev = async (options, { resolve, reject }) => {
512
586
  __dirname
513
587
  );
514
588
  if (options?.environment === "test") {
515
- process.loader.text("Running tests...");
589
+ process.loader.stop();
590
+ const databaseProcessIds = getDatabaseProcessIds();
516
591
  await runTests({
517
- __dirname
592
+ watch: options?.watch,
593
+ __dirname,
594
+ processIds: [
595
+ ...processIds,
596
+ ...databaseProcessIds
597
+ ],
598
+ cleanupProcess: process.cleanupProcess
518
599
  });
600
+ if (!options?.watch) {
601
+ process.exit(0);
602
+ }
519
603
  }
520
604
  resolve();
521
605
  } catch (exception) {
@@ -1,7 +1,7 @@
1
1
  import fs from "fs";
2
2
  import CLILog from "../CLILog.js";
3
3
  import isValidJSONString from "../isValidJSONString.js";
4
- const warnIfInvalidJSONInSettings = (settings = "") => {
4
+ const warnIfInvalidJSONInSettings = (settings = "", processIds = []) => {
5
5
  try {
6
6
  const isValidJSON = isValidJSONString(settings);
7
7
  const context = process.env.NODE_ENV === "test" ? "test" : "start";
@@ -14,6 +14,9 @@ const warnIfInvalidJSONInSettings = (settings = "") => {
14
14
  tools: [{ title: "JSON Linter", url: "https://jsonlint.com/" }]
15
15
  }
16
16
  );
17
+ if (process.cleanupProcess) {
18
+ process.cleanupProcess.send(JSON.stringify({ processIds }));
19
+ }
17
20
  process.exit(0);
18
21
  }
19
22
  } catch (exception) {
@@ -27,7 +30,7 @@ const getSettings = (settingsPath = "") => {
27
30
  throw new Error(`[loadSettings.getSettings] ${exception.message}`);
28
31
  }
29
32
  };
30
- const warnIfSettingsNotFound = (settingsPath = "") => {
33
+ const warnIfSettingsNotFound = (settingsPath = "", processIds = []) => {
31
34
  try {
32
35
  const hasSettingsFile = fs.existsSync(settingsPath);
33
36
  const context = process.env.NODE_ENV === "test" ? "test" : "start";
@@ -39,6 +42,9 @@ const warnIfSettingsNotFound = (settingsPath = "") => {
39
42
  docs: `https://cheatcode.co/docs/joystick/cli/${context}`
40
43
  }
41
44
  );
45
+ if (process.cleanupProcess) {
46
+ process.cleanupProcess.send(JSON.stringify({ processIds }));
47
+ }
42
48
  process.exit(0);
43
49
  }
44
50
  } catch (exception) {
@@ -59,9 +65,9 @@ const loadSettings = (options, { resolve, reject }) => {
59
65
  try {
60
66
  validateOptions(options);
61
67
  const settingsPath = `${process.cwd()}/settings.${options.environment}.json`;
62
- warnIfSettingsNotFound(settingsPath);
68
+ warnIfSettingsNotFound(settingsPath, options.processIds);
63
69
  const settings = getSettings(settingsPath);
64
- warnIfInvalidJSONInSettings(settings);
70
+ warnIfInvalidJSONInSettings(settings, options.processIds);
65
71
  process.env.JOYSTICK_SETTINGS = settings;
66
72
  resolve({
67
73
  parsed: JSON.parse(settings),
@@ -1,39 +1,71 @@
1
1
  import child_process from "child_process";
2
- const handleAvaSTDIO = (ava = {}) => {
2
+ import CLILog from "../CLILog.js";
3
+ const handleAvaSTDERR = (stderr = "", options = {}) => {
4
+ try {
5
+ if (stderr?.includes("Using configuration")) {
6
+ return null;
7
+ }
8
+ if (stderr?.includes("No tests found")) {
9
+ return CLILog("No tests found. Add tests in the /tests folder at the root of your Joystick app.", {
10
+ level: "danger",
11
+ docs: "https://cheatcode.co/docs/joystick/test/setup"
12
+ });
13
+ }
14
+ console.log(stderr);
15
+ } catch (exception) {
16
+ throw new Error(`[runTests.handleAvaSTDERR] ${exception.message}`);
17
+ }
18
+ };
19
+ const handleAvaSTDOUT = (stdout = "", options = {}) => {
20
+ try {
21
+ if (stdout?.includes("Using configuration")) {
22
+ return null;
23
+ }
24
+ if (stdout?.includes("No tests found in")) {
25
+ const [message] = stdout?.split(",");
26
+ return console.log(`${message}
27
+ `);
28
+ }
29
+ console.log(stdout);
30
+ } catch (exception) {
31
+ throw new Error(`[runTests.handleAvaSTDOUT] ${exception.message}`);
32
+ }
33
+ };
34
+ const handleAvaSTDIO = (ava = {}, options = {}) => {
3
35
  try {
4
36
  ava.stdout.on("data", function(data) {
5
37
  const string = data.toString();
6
- if (!string?.includes("ava")) {
7
- console.log(string);
8
- }
38
+ handleAvaSTDOUT(string, options);
9
39
  });
10
40
  ava.stderr.on("data", function(data) {
11
41
  const string = data.toString();
12
- if (!string?.includes("ava")) {
13
- console.log(string);
14
- }
42
+ handleAvaSTDERR(string, options);
15
43
  });
16
44
  } catch (exception) {
17
45
  throw new Error(`[runTests.handleAvaSTDIO] ${exception.message}`);
18
46
  }
19
47
  };
20
- const runAva = (__dirname = "") => {
48
+ const runAva = (options = {}) => {
21
49
  try {
22
- console.log(`node_modules/.bin/ava --config ${__dirname}/tests.config.js`);
50
+ const avaPath = `${process.cwd()}/node_modules/.bin/ava`;
23
51
  return new Promise((resolve, reject) => {
24
- const ava = child_process.exec(`node_modules/.bin/ava --config ${__dirname}/tests.config.js`, {
52
+ const ava = child_process.exec(`${avaPath} --config ${options?.__dirname}/tests.config.js ${options?.watch ? "--watch" : ""}`, {
25
53
  stdio: "inherit",
26
54
  env: {
27
55
  ...process.env,
56
+ databases: process.databases,
28
57
  FORCE_COLOR: "1"
29
58
  }
30
59
  }, (error) => {
31
- if (error) {
32
- return reject();
60
+ if (!error) {
61
+ options.cleanupProcess.send(JSON.stringify({ processIds: options?.processIds }));
62
+ resolve();
63
+ } else {
64
+ options.cleanupProcess.send(JSON.stringify({ processIds: options?.processIds }));
65
+ resolve();
33
66
  }
34
- return resolve();
35
67
  });
36
- handleAvaSTDIO(ava);
68
+ handleAvaSTDIO(ava, options);
37
69
  });
38
70
  } catch (exception) {
39
71
  throw new Error(`[runTests.runAva] ${exception.message}`);
@@ -52,7 +84,7 @@ const validateOptions = (options) => {
52
84
  const runTests = async (options, { resolve, reject }) => {
53
85
  try {
54
86
  validateOptions(options);
55
- await runAva(options?.__dirname);
87
+ await runAva(options);
56
88
  resolve();
57
89
  } catch (exception) {
58
90
  reject(`[runTests] ${exception.message}`);
@@ -1,8 +1,10 @@
1
1
  import child_process from "child_process";
2
2
  import path from "path";
3
- const handleStartServerProcess = (execArgv = {}, sessionsBeforeHMRUpdate = {}) => {
3
+ const handleStartServerProcess = (execArgv = {}, options = {}) => {
4
4
  try {
5
- process.loader.text("Starting app...");
5
+ if (!options?.watch) {
6
+ process.loader.text("Starting app...");
7
+ }
6
8
  return child_process.fork(
7
9
  path.resolve(".joystick/build/index.server.js"),
8
10
  [],
@@ -18,7 +20,7 @@ const handleStartServerProcess = (execArgv = {}, sessionsBeforeHMRUpdate = {}) =
18
20
  ROOT_URL: process.env.ROOT_URL,
19
21
  PORT: process.env.PORT,
20
22
  JOYSTICK_SETTINGS: process.env.JOYSTICK_SETTINGS,
21
- HMR_SESSIONS: JSON.stringify(sessionsBeforeHMRUpdate)
23
+ HMR_SESSIONS: options?.sessionsBeforeHMRUpdate || "{}"
22
24
  }
23
25
  }
24
26
  );
@@ -56,7 +58,7 @@ const startApp = (options, { resolve, reject }) => {
56
58
  try {
57
59
  validateOptions(options);
58
60
  const execArgv = getExecArgs(options?.nodeMajorVersion);
59
- const serverProcess = handleStartServerProcess(execArgv, options?.sessionsBeforeHMRUpdate);
61
+ const serverProcess = handleStartServerProcess(execArgv, options);
60
62
  return resolve(serverProcess);
61
63
  } catch (exception) {
62
64
  reject(`[startApp] ${exception.message}`);
@@ -0,0 +1,12 @@
1
+ var tests_config_default = {
2
+ files: [`tests/**/*.test.js`],
3
+ // NOTE: Intentionally limit to 1 test at a time to avoid concurrency creating
4
+ // race conditions in tests that have users or other "global" data that needs
5
+ // to be cleaned up. The extra time needed is worth it to guarantee that tests
6
+ // don't step on each other's toes and our code is properly verified.
7
+ concurrency: 1,
8
+ cache: false
9
+ };
10
+ export {
11
+ tests_config_default as default
12
+ };
@@ -1,3 +1,4 @@
1
+ const EXAMPLE_CODE_REGEX = new RegExp(/\<example\>[^%]+\<\/example\>/g);
1
2
  const EXPORT_DEFAULT_REGEX = new RegExp(/export default [a-zA-Z0-9]+/g);
2
3
  const JOYSTICK_COMMENT_REGEX = new RegExp(/\<\!\-\-(.|\n|\r)*?-->/g);
3
4
  const JOYSTICK_COMPONENT_REGEX = new RegExp(/\.component\(\{/g);
@@ -5,6 +6,7 @@ const JOYSTICK_UI_REGEX = new RegExp(/@joystick.js\/ui/g);
5
6
  const OBJECT_REGEX = new RegExp(/{([^;]*)}/g);
6
7
  const SETTINGS_FILE_NAME_REGEX = new RegExp(/settings.[a-zA-Z0-9]+.json/g);
7
8
  export {
9
+ EXAMPLE_CODE_REGEX,
8
10
  EXPORT_DEFAULT_REGEX,
9
11
  JOYSTICK_COMMENT_REGEX,
10
12
  JOYSTICK_COMPONENT_REGEX,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@joystick.js/cli-canary",
3
- "version": "0.0.0-canary.99",
3
+ "version": "1.0.0-beta.75",
4
4
  "type": "module",
5
5
  "description": "CLI for the Joystick JavaScript framework.",
6
6
  "main": "development.js",
@@ -25,6 +25,7 @@
25
25
  "@babel/code-frame": "^7.15.8",
26
26
  "acorn": "^8.5.0",
27
27
  "ascii-table": "^0.0.9",
28
+ "ava": "^5.3.1",
28
29
  "chalk": "^4.1.2",
29
30
  "chokidar": "^3.5.2",
30
31
  "command-exists": "^1.2.9",
@@ -224,21 +224,26 @@ export default {
224
224
  set: !!rawArgs.includes('test'),
225
225
  description: 'Start an existing Joystick app and run its tests.',
226
226
  args: {
227
- w: {
228
- set: !!rawArgs.includes('w') && !!rawArgs[rawArgs.indexOf('w') + 1],
229
- parent: 'w',
230
- value: !!rawArgs.includes('w') && rawArgs[rawArgs.indexOf('w') + 1],
227
+ watch: {
228
+ set: !!rawArgs.includes('watch'),
229
+ parent: 'test',
230
+ value: !!rawArgs.includes('watch'),
231
231
  description: 'Run joystick test in watch mode.',
232
232
  },
233
+ },
234
+ options: {
233
235
  watch: {
234
- set: !!rawArgs.includes('watch') && !!rawArgs[rawArgs.indexOf('watch') + 1],
235
- parent: 'watch',
236
- value: !!rawArgs.includes('watch') && rawArgs[rawArgs.indexOf('watch') + 1],
237
- description: 'Run joystick test in watch mode.',
236
+ flags: {
237
+ '--watch': {
238
+ set: !!rawArgs.includes('--watch'),
239
+ value: !!rawArgs.includes('--watch'),
240
+ parent: 'test'
241
+ },
242
+ },
243
+ description: 'Environment to set for process.env.NODE_ENV.',
238
244
  },
239
245
  },
240
- options: {},
241
- function: test(),
246
+ function: test,
242
247
  },
243
248
  update: {
244
249
  set: !!rawArgs.includes('update'),