runner.js 4.3 KB

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