main.ts 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. import { app, dialog } from 'electron';
  2. import * as fs from 'fs';
  3. import * as path from 'path';
  4. import * as url from 'url';
  5. type DefaultAppOptions = {
  6. file: null | string;
  7. noHelp: boolean;
  8. version: boolean;
  9. webdriver: boolean;
  10. interactive: boolean;
  11. abi: boolean;
  12. modules: string[];
  13. }
  14. const Module = require('module');
  15. // Parse command line options.
  16. const argv = process.argv.slice(1);
  17. const option: DefaultAppOptions = {
  18. file: null,
  19. noHelp: Boolean(process.env.ELECTRON_NO_HELP),
  20. version: false,
  21. webdriver: false,
  22. interactive: false,
  23. abi: false,
  24. modules: []
  25. };
  26. let nextArgIsRequire = false;
  27. for (const arg of argv) {
  28. if (nextArgIsRequire) {
  29. option.modules.push(arg);
  30. nextArgIsRequire = false;
  31. continue;
  32. } else if (arg === '--version' || arg === '-v') {
  33. option.version = true;
  34. break;
  35. } else if (arg.match(/^--app=/)) {
  36. option.file = arg.split('=')[1];
  37. break;
  38. } else if (arg === '--interactive' || arg === '-i' || arg === '-repl') {
  39. option.interactive = true;
  40. } else if (arg === '--test-type=webdriver') {
  41. option.webdriver = true;
  42. } else if (arg === '--require' || arg === '-r') {
  43. nextArgIsRequire = true;
  44. continue;
  45. } else if (arg === '--abi' || arg === '-a') {
  46. option.abi = true;
  47. continue;
  48. } else if (arg === '--no-help') {
  49. option.noHelp = true;
  50. continue;
  51. } else if (arg[0] === '-') {
  52. continue;
  53. } else {
  54. option.file = arg;
  55. break;
  56. }
  57. }
  58. if (nextArgIsRequire) {
  59. console.error('Invalid Usage: --require [file]\n\n"file" is required');
  60. process.exit(1);
  61. }
  62. // Set up preload modules
  63. if (option.modules.length > 0) {
  64. Module._preloadModules(option.modules);
  65. }
  66. function loadApplicationPackage (packagePath: string) {
  67. // Add a flag indicating app is started from default app.
  68. Object.defineProperty(process, 'defaultApp', {
  69. configurable: false,
  70. enumerable: true,
  71. value: true
  72. });
  73. try {
  74. // Override app name and version.
  75. packagePath = path.resolve(packagePath);
  76. const packageJsonPath = path.join(packagePath, 'package.json');
  77. let appPath;
  78. if (fs.existsSync(packageJsonPath)) {
  79. let packageJson;
  80. try {
  81. packageJson = require(packageJsonPath);
  82. } catch (e) {
  83. showErrorMessage(`Unable to parse ${packageJsonPath}\n\n${e.message}`);
  84. return;
  85. }
  86. if (packageJson.version) {
  87. app.setVersion(packageJson.version);
  88. }
  89. if (packageJson.productName) {
  90. app.name = packageJson.productName;
  91. } else if (packageJson.name) {
  92. app.name = packageJson.name;
  93. }
  94. appPath = packagePath;
  95. }
  96. try {
  97. const filePath = Module._resolveFilename(packagePath, module, true);
  98. app._setDefaultAppPaths(appPath || path.dirname(filePath));
  99. } catch (e) {
  100. showErrorMessage(`Unable to find Electron app at ${packagePath}\n\n${e.message}`);
  101. return;
  102. }
  103. // Run the app.
  104. Module._load(packagePath, module, true);
  105. } catch (e) {
  106. console.error('App threw an error during load');
  107. console.error(e.stack || e);
  108. throw e;
  109. }
  110. }
  111. function showErrorMessage (message: string) {
  112. app.focus();
  113. dialog.showErrorBox('Error launching app', message);
  114. process.exit(1);
  115. }
  116. async function loadApplicationByURL (appUrl: string) {
  117. const { loadURL } = await import('./default_app');
  118. loadURL(appUrl);
  119. }
  120. async function loadApplicationByFile (appPath: string) {
  121. const { loadFile } = await import('./default_app');
  122. loadFile(appPath);
  123. }
  124. function startRepl () {
  125. if (process.platform === 'win32') {
  126. console.error('Electron REPL not currently supported on Windows');
  127. process.exit(1);
  128. }
  129. // prevent quitting
  130. app.on('window-all-closed', () => {});
  131. const repl = require('repl');
  132. repl.start('> ').on('exit', () => {
  133. process.exit(0);
  134. });
  135. }
  136. // Start the specified app if there is one specified in command line, otherwise
  137. // start the default app.
  138. if (option.file && !option.webdriver) {
  139. const file = option.file;
  140. const protocol = url.parse(file).protocol;
  141. const extension = path.extname(file);
  142. if (protocol === 'http:' || protocol === 'https:' || protocol === 'file:' || protocol === 'chrome:') {
  143. loadApplicationByURL(file);
  144. } else if (extension === '.html' || extension === '.htm') {
  145. loadApplicationByFile(path.resolve(file));
  146. } else {
  147. loadApplicationPackage(file);
  148. }
  149. } else if (option.version) {
  150. console.log('v' + process.versions.electron);
  151. process.exit(0);
  152. } else if (option.abi) {
  153. console.log(process.versions.modules);
  154. process.exit(0);
  155. } else if (option.interactive) {
  156. startRepl();
  157. } else {
  158. if (!option.noHelp) {
  159. const welcomeMessage = `
  160. Electron ${process.versions.electron} - Build cross platform desktop apps with JavaScript, HTML, and CSS
  161. Usage: electron [options] [path]
  162. A path to an Electron app may be specified. It must be one of the following:
  163. - index.js file.
  164. - Folder containing a package.json file.
  165. - Folder containing an index.js file.
  166. - .html/.htm file.
  167. - http://, https://, or file:// URL.
  168. Options:
  169. -i, --interactive Open a REPL to the main process.
  170. -r, --require Module to preload (option can be repeated).
  171. -v, --version Print the version.
  172. -a, --abi Print the Node ABI version.`;
  173. console.log(welcomeMessage);
  174. }
  175. loadApplicationByFile('index.html');
  176. }