runner.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. const
  2. chokidar = require('chokidar'),
  3. debounce = require('lodash.debounce'),
  4. path = require('path'),
  5. { readFileSync, writeFileSync } = require('fs-extra')
  6. const
  7. { spawn } = require('./helpers/spawn'),
  8. log = require('./helpers/logger')('app:tauri'),
  9. onShutdown = require('./helpers/on-shutdown'),
  10. generator = require('./generator'),
  11. { tauriDir } = require('./helpers/app-paths')
  12. class Runner {
  13. constructor() {
  14. this.pid = 0
  15. this.tauriWatcher = null
  16. onShutdown(() => {
  17. this.stop()
  18. })
  19. }
  20. async run(cfg) {
  21. process.env.TAURI_DIST_DIR = cfg.build.distDir
  22. process.env.TAURI_CONFIG_DIR = tauriDir
  23. const url = cfg.build.APP_URL
  24. if (this.pid) {
  25. if (this.url !== url) {
  26. await this.stop()
  27. } else {
  28. return
  29. }
  30. }
  31. this.__manipulateToml(toml => {
  32. this.__whitelistApi(cfg, toml)
  33. })
  34. generator.generate(cfg.tauri)
  35. this.url = url
  36. const args = ['--url', url]
  37. const startDevTauri = () => {
  38. return this.__runCargoCommand({
  39. cargoArgs: ['run', '--features', 'dev'],
  40. extraArgs: args
  41. })
  42. }
  43. // Start watching for tauri app changes
  44. this.tauriWatcher = chokidar
  45. .watch([
  46. path.join(tauriDir, 'src'),
  47. path.join(tauriDir, 'Cargo.toml'),
  48. path.join(tauriDir, 'build.rs')
  49. ], {
  50. watchers: {
  51. chokidar: {
  52. ignoreInitial: true
  53. }
  54. }
  55. })
  56. .on('change', debounce(async () => {
  57. await this.__stopCargo()
  58. startDevTauri()
  59. }, 1000))
  60. return startDevTauri()
  61. }
  62. async build(cfg) {
  63. process.env.TAURI_DIST_DIR = cfg.build.distDir
  64. process.env.TAURI_CONFIG_DIR = tauriDir
  65. this.__manipulateToml(toml => {
  66. this.__whitelistApi(cfg, toml)
  67. })
  68. generator.generate(cfg.tauri)
  69. const features = []
  70. if (cfg.tauri.embeddedServer.active) {
  71. features.push('embedded-server')
  72. }
  73. const buildFn = target => this.__runCargoCommand({
  74. cargoArgs: [cfg.tauri.bundle.active ? 'tauri-bundle' : 'build']
  75. .concat(features.length ? ['--features', ...features] : [])
  76. .concat(cfg.ctx.debug ? [] : ['--release'])
  77. .concat(target ? ['--target', target] : [])
  78. })
  79. if (cfg.ctx.debug || !cfg.ctx.targetName) {
  80. // on debug mode or if no target specified,
  81. // build only for the current platform
  82. return buildFn()
  83. }
  84. const targets = cfg.ctx.target.split(',')
  85. for (const target of targets) {
  86. await buildFn(target)
  87. }
  88. }
  89. stop() {
  90. return new Promise((resolve, reject) => {
  91. this.tauriWatcher && this.tauriWatcher.close()
  92. this.__stopCargo().then(resolve)
  93. })
  94. }
  95. __runCargoCommand({
  96. cargoArgs,
  97. extraArgs
  98. }) {
  99. return new Promise(resolve => {
  100. this.pid = spawn(
  101. 'cargo',
  102. extraArgs ?
  103. cargoArgs.concat(['--']).concat(extraArgs) :
  104. cargoArgs,
  105. tauriDir,
  106. code => {
  107. if (code) {
  108. warn()
  109. warn(`⚠️ [FAIL] Cargo CLI has failed`)
  110. warn()
  111. process.exit(1)
  112. }
  113. if (this.killPromise) {
  114. this.killPromise()
  115. this.killPromise = null
  116. } else { // else it wasn't killed by us
  117. warn()
  118. warn('Cargo process was killed. Exiting...')
  119. warn()
  120. process.exit(0)
  121. }
  122. }
  123. )
  124. resolve()
  125. })
  126. }
  127. __stopCargo() {
  128. const pid = this.pid
  129. if (!pid) {
  130. return Promise.resolve()
  131. }
  132. log('Shutting down tauri process...')
  133. this.pid = 0
  134. return new Promise((resolve, reject) => {
  135. this.killPromise = resolve
  136. process.kill(pid)
  137. })
  138. }
  139. __manipulateToml(callback) {
  140. const toml = require('@iarna/toml'),
  141. tomlPath = path.join(tauriDir, 'Cargo.toml'),
  142. tomlFile = readFileSync(tomlPath),
  143. tomlContents = toml.parse(tomlFile)
  144. callback(tomlContents)
  145. const output = toml.stringify(tomlContents)
  146. writeFileSync(tomlPath, output)
  147. }
  148. __whitelistApi(cfg, tomlContents) {
  149. if (!tomlContents.dependencies.tauri.features) {
  150. tomlContents.dependencies.tauri.features = []
  151. }
  152. if (cfg.tauri.whitelist.all) {
  153. if (!tomlContents.dependencies.tauri.features.includes('all-api')) {
  154. tomlContents.dependencies.tauri.features.push('all-api')
  155. }
  156. } else {
  157. const whitelist = Object.keys(cfg.tauri.whitelist).filter(w => cfg.tauri.whitelist[w] === true)
  158. tomlContents.dependencies.tauri.features = whitelist.concat(tomlContents.dependencies.tauri.features.filter(f => f !== 'api' && cfg.tauri.whitelist[f] !== true))
  159. }
  160. }
  161. }
  162. module.exports = Runner