run-selfhosted.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. // surrounding_box function
  2. //
  3. // It's really hard to see an error message without using
  4. // the surrounding_box function to highlight its location.
  5. // The implementation of this in packages/backend might not
  6. // work in older versions of node, so we instead re-implement
  7. // it here.
  8. import console from 'node:console';
  9. import process from 'node:process';
  10. const surrounding_box = (col, lines) => {
  11. const lengths = lines.map(line => line.length);
  12. const max_length = Math.max(...lengths);
  13. const c = str => `\x1b[${col}m${str}\x1b[0m`;
  14. const bar = c(Array(max_length + 4).fill('━').join(''));
  15. for ( let i = 0 ; i < lines.length ; i++ ) {
  16. while ( lines[i].length < max_length ) {
  17. lines[i] += ' ';
  18. }
  19. lines[i] = `${c('┃ ')} ${lines[i]} ${c(' ┃')}`;
  20. }
  21. lines.unshift(`${c('┏')}${bar}${c('┓')}`);
  22. lines.push(`${c('┗')}${bar}${c('┛')}`);
  23. };
  24. // node version check
  25. {
  26. // Keeping track of WHY certain versions don't work
  27. const ver_info = [
  28. { under: 14, reasons: ['optional chaining is not available'] },
  29. { under: 16, reasons: ['disk usage package ABI mismatch'] },
  30. ];
  31. const lowest_allowed = Math.max(...ver_info.map(r => r.under));
  32. // ACTUAL VERSION CHECK
  33. const [major, minor] = process.versions.node.split('.').map(Number);
  34. if ( major < lowest_allowed ) {
  35. const lines = [];
  36. lines.push(`Please use a version of Node.js ${lowest_allowed} or newer.`);
  37. lines.push(`Issues with node ${process.versions.node}:`);
  38. // We also show the user the reasons in case they want to know
  39. for ( const { under, reasons } of ver_info ) {
  40. if ( major < under ) {
  41. lines.push(` - ${reasons.join(', ')}`);
  42. }
  43. }
  44. surrounding_box('31;1', lines);
  45. console.error(lines.join('\n'));
  46. process.exit(1);
  47. }
  48. }
  49. // Annoying polyfill for inconsistency in different node versions
  50. if ( ! import.meta.filename ) {
  51. Object.defineProperty(import.meta, 'filename', {
  52. get: () => import.meta.url.slice('file://'.length),
  53. })
  54. }
  55. const main = async () => {
  56. const {
  57. Kernel,
  58. CoreModule,
  59. DatabaseModule,
  60. LocalDiskStorageModule,
  61. SelfHostedModule
  62. } = (await import('@heyputer/backend')).default;
  63. const k = new Kernel({
  64. entry_path: import.meta.filename
  65. });
  66. k.add_module(new CoreModule());
  67. k.add_module(new DatabaseModule());
  68. k.add_module(new LocalDiskStorageModule());
  69. k.add_module(new SelfHostedModule());
  70. k.boot();
  71. };
  72. const early_init_errors = [
  73. {
  74. text: `Cannot find package '@heyputer/backend'`,
  75. notes: [
  76. 'this usually happens if you forget `npm install`'
  77. ],
  78. suggestions: [
  79. 'try running `npm install`'
  80. ],
  81. technical_notes: [
  82. '@heyputer/backend is in an npm workspace'
  83. ]
  84. },
  85. {
  86. text: `Cannot find package`,
  87. notes: [
  88. 'this usually happens if you forget `npm install`'
  89. ],
  90. suggestions: [
  91. 'try running `npm install`'
  92. ],
  93. }
  94. ];
  95. // null coalescing operator
  96. const nco = (...args) => {
  97. for ( const arg of args ) {
  98. if ( arg !== undefined && arg !== null ) {
  99. return arg;
  100. }
  101. }
  102. return undefined;
  103. }
  104. const _print_error_help = (error_help) => {
  105. const lines = [];
  106. lines.push(nco(error_help.title, error_help.text));
  107. for ( const note of (nco(error_help.notes, [])) ) {
  108. lines.push(`📝 ${note}`)
  109. }
  110. if ( error_help.suggestions ) {
  111. lines.push('Suggestions:');
  112. for ( const suggestion of error_help.suggestions ) {
  113. lines.push(`- ${suggestion}`);
  114. }
  115. }
  116. if ( error_help.technical_notes ) {
  117. lines.push('Technical Notes:');
  118. for ( const note of error_help.technical_notes ) {
  119. lines.push(`- ${note}`);
  120. }
  121. }
  122. surrounding_box('31;1', lines);
  123. console.error(lines.join('\n'));
  124. }
  125. (async () => {
  126. try {
  127. await main();
  128. } catch (e) {
  129. for ( const error_help of early_init_errors ) {
  130. const message = e && e.message;
  131. if ( e.message && e.message.includes(error_help.text) ) {
  132. _print_error_help(error_help);
  133. break;
  134. }
  135. }
  136. throw e;
  137. }
  138. })();