runner.js 4.9 KB

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