runner.js 4.8 KB

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