platform.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. 'use strict'
  2. const asar = require('asar')
  3. const debug = require('debug')('electron-packager')
  4. const fs = require('fs-extra')
  5. const path = require('path')
  6. const pify = require('pify')
  7. const common = require('./common')
  8. const hooks = require('./hooks')
  9. const ignore = require('./ignore')
  10. class App {
  11. constructor (opts, templatePath) {
  12. this.opts = opts
  13. this.templatePath = templatePath
  14. if (this.opts.prune === undefined) {
  15. this.opts.prune = true
  16. }
  17. }
  18. /**
  19. * Resource directory path before renaming.
  20. */
  21. get originalResourcesDir () {
  22. return this.resourcesDir
  23. }
  24. /**
  25. * Resource directory path after renaming.
  26. */
  27. get resourcesDir () {
  28. return path.join(this.stagingPath, 'resources')
  29. }
  30. get originalResourcesAppDir () {
  31. return path.join(this.originalResourcesDir, 'app')
  32. }
  33. get electronBinaryDir () {
  34. return this.stagingPath
  35. }
  36. get originalElectronName () {
  37. /* istanbul ignore next */
  38. throw new Error('Child classes must implement this')
  39. }
  40. get newElectronName () {
  41. /* istanbul ignore next */
  42. throw new Error('Child classes must implement this')
  43. }
  44. get executableName () {
  45. return this.opts.executableName || this.opts.name
  46. }
  47. get stagingPath () {
  48. if (this.opts.tmpdir === false) {
  49. return common.generateFinalPath(this.opts)
  50. } else {
  51. return path.join(
  52. common.baseTempDir(this.opts),
  53. `${this.opts.platform}-${this.opts.arch}`,
  54. common.generateFinalBasename(this.opts)
  55. )
  56. }
  57. }
  58. relativeRename (basePath, oldName, newName) {
  59. debug(`Renaming ${oldName} to ${newName} in ${basePath}`)
  60. return fs.rename(path.join(basePath, oldName), path.join(basePath, newName))
  61. }
  62. renameElectron () {
  63. return this.relativeRename(this.electronBinaryDir, this.originalElectronName, this.newElectronName)
  64. }
  65. /**
  66. * Performs the following initial operations for an app:
  67. * * Creates temporary directory
  68. * * Copies template into temporary directory
  69. * * Copies user's app into temporary directory
  70. * * Prunes non-production node_modules (if opts.prune is either truthy or undefined)
  71. * * Remove default_app (which is either a folder or an asar file)
  72. * * Creates an asar (if opts.asar is set)
  73. *
  74. * Prune and asar are performed before platform-specific logic, primarily so that
  75. * this.originalResourcesAppDir is predictable (e.g. before .app is renamed for Mac)
  76. */
  77. initialize () {
  78. debug(`Initializing app in ${this.stagingPath} from ${this.templatePath} template`)
  79. return fs.move(this.templatePath, this.stagingPath, { clobber: true })
  80. .then(() => this.copyTemplate())
  81. .then(() => this.removeDefaultApp())
  82. .then(() => this.asarApp())
  83. }
  84. copyTemplate () {
  85. return fs.copy(this.opts.dir, this.originalResourcesAppDir, {
  86. filter: ignore.userIgnoreFilter(this.opts),
  87. dereference: this.opts.derefSymlinks
  88. }).then(() => hooks.promisifyHooks(this.opts.afterCopy, [
  89. this.originalResourcesAppDir,
  90. this.opts.electronVersion,
  91. this.opts.platform,
  92. this.opts.arch
  93. ])).then(() => {
  94. if (this.opts.prune) {
  95. return hooks.promisifyHooks(this.opts.afterPrune, [
  96. this.originalResourcesAppDir,
  97. this.opts.electronVersion,
  98. this.opts.platform,
  99. this.opts.arch
  100. ])
  101. } else {
  102. return true
  103. }
  104. })
  105. }
  106. removeDefaultApp () {
  107. return fs.remove(path.join(this.originalResourcesDir, 'default_app'))
  108. .then(() => fs.remove(path.join(this.originalResourcesDir, 'default_app.asar')))
  109. }
  110. /**
  111. * Forces an icon filename to a given extension and returns the normalized filename,
  112. * if it exists. Otherwise, returns null.
  113. *
  114. * This error path is used by win32 if no icon is specified.
  115. */
  116. normalizeIconExtension (targetExt) {
  117. if (!this.opts.icon) throw new Error('No filename specified to normalizeExt')
  118. let iconFilename = this.opts.icon
  119. const ext = path.extname(iconFilename)
  120. if (ext !== targetExt) {
  121. iconFilename = path.join(path.dirname(iconFilename), path.basename(iconFilename, ext) + targetExt)
  122. }
  123. return fs.pathExists(iconFilename)
  124. .then(() => iconFilename)
  125. .catch(/* istanbul ignore next */ () => null)
  126. }
  127. asarApp () {
  128. const asarOptions = common.createAsarOpts(this.opts)
  129. if (!asarOptions) {
  130. return Promise.resolve()
  131. }
  132. const dest = path.join(this.originalResourcesDir, 'app.asar')
  133. debug(`Running asar with the options ${JSON.stringify(asarOptions)}`)
  134. return pify(asar.createPackageWithOptions)(this.originalResourcesAppDir, dest, asarOptions)
  135. .then(() => fs.remove(this.originalResourcesAppDir))
  136. }
  137. copyExtraResources () {
  138. if (!this.opts.extraResource) return Promise.resolve()
  139. const extraResources = common.ensureArray(this.opts.extraResource)
  140. return Promise.all(extraResources.map(
  141. resource => fs.copy(resource, path.resolve(this.stagingPath, this.resourcesDir, path.basename(resource)))
  142. ))
  143. }
  144. move () {
  145. const finalPath = common.generateFinalPath(this.opts)
  146. if (this.opts.tmpdir === false) {
  147. return Promise.resolve(finalPath)
  148. }
  149. debug(`Moving ${this.stagingPath} to ${finalPath}`)
  150. return fs.move(this.stagingPath, finalPath)
  151. .then(() => finalPath)
  152. }
  153. }
  154. module.exports = App