index.js 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. import * as path from 'node:path';
  2. import {fileURLToPath} from 'node:url';
  3. import chokidar from 'chokidar';
  4. import {colors, logError, logInfo, logWarn, parseOptions} from '#cli';
  5. import {isMain} from '#node-utils';
  6. import {getContextAssignments} from '#repl';
  7. import {bindOpts, showAggregate} from '#sugar';
  8. import {quickLoadAllFromYAML} from '#yaml';
  9. async function main() {
  10. const miscOptions = await parseOptions(process.argv.slice(2), {
  11. 'data-path': {
  12. type: 'value',
  13. },
  14. });
  15. const dataPath = miscOptions['data-path'] || process.env.HSMUSIC_DATA;
  16. if (!dataPath) {
  17. logError`Expected --data-path option or HSMUSIC_DATA to be set`;
  18. return;
  19. }
  20. console.log(`HSMusic automated data tests`);
  21. console.log(`${colors.bright(colors.yellow(`:star:`))} Now featuring quick-reloading! ${colors.bright(colors.cyan(`:earth:`))}`);
  22. // Watch adjacent files in data-tests directory
  23. const metaPath = fileURLToPath(import.meta.url);
  24. const metaDirname = path.dirname(metaPath);
  25. const watcher = chokidar.watch(metaDirname);
  26. const wikiData = await quickLoadAllFromYAML(dataPath, {
  27. showAggregate: bindOpts(showAggregate, {
  28. showTraces: false,
  29. }),
  30. });
  31. const context = await getContextAssignments({
  32. wikiData,
  33. });
  34. let resolveNext;
  35. const queue = [];
  36. watcher.on('all', (event, path) => {
  37. if (!['add', 'change'].includes(event)) return;
  38. if (path === metaPath) return;
  39. if (resolveNext) {
  40. resolveNext(path);
  41. } else if (!queue.includes(path)) {
  42. queue.push(path);
  43. }
  44. });
  45. logInfo`Awaiting file changes.`;
  46. /* eslint-disable-next-line no-constant-condition */
  47. while (true) {
  48. const testPath = (queue.length
  49. ? queue.shift()
  50. : await new Promise(resolve => {
  51. resolveNext = resolve;
  52. }));
  53. resolveNext = null;
  54. const shortPath = path.basename(testPath);
  55. logInfo`Path updated: ${shortPath} - running this test!`;
  56. let imp;
  57. try {
  58. imp = await import(`${testPath}?${Date.now()}`)
  59. } catch (error) {
  60. logWarn`Failed to import ${shortPath} - ${error.constructor.name} details below:`;
  61. console.error(error);
  62. continue;
  63. }
  64. const {default: testFn} = imp;
  65. if (!testFn) {
  66. logWarn`No default export for ${shortPath}`;
  67. logWarn`Skipping this test for now!`;
  68. continue;
  69. }
  70. if (typeof testFn !== 'function') {
  71. logWarn`Default export for ${shortPath} is ${typeof testFn}, not function`;
  72. logWarn`Skipping this test for now!`;
  73. continue;
  74. }
  75. try {
  76. await testFn(context);
  77. } catch (error) {
  78. showAggregate(error, {
  79. pathToFileURL: f => path.relative(metaDirname, fileURLToPath(f)),
  80. });
  81. }
  82. }
  83. }
  84. if (isMain(import.meta.url)) {
  85. main().catch((error) => {
  86. if (error instanceof AggregateError) {
  87. showAggregate(error);
  88. } else {
  89. console.error(error);
  90. }
  91. });
  92. }