runner.js 4.7 KB

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