infer.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. 'use strict'
  2. const debug = require('debug')('electron-packager')
  3. const getPackageInfo = require('get-package-info')
  4. const parseAuthor = require('parse-author')
  5. const path = require('path')
  6. const pify = require('pify')
  7. const resolve = require('resolve')
  8. function isMissingRequiredProperty (props) {
  9. return props.some(prop => prop === 'productName' || prop === 'dependencies.electron')
  10. }
  11. function errorMessageForProperty (prop) {
  12. let hash, propDescription
  13. switch (prop) {
  14. case 'productName':
  15. hash = 'name'
  16. propDescription = 'application name'
  17. break
  18. case 'dependencies.electron':
  19. hash = 'electronversion'
  20. propDescription = 'Electron version'
  21. break
  22. default:
  23. hash = ''
  24. propDescription = '[Unknown Property]'
  25. }
  26. return `Unable to determine ${propDescription}. Please specify an ${propDescription}\n\n` +
  27. 'For more information, please see\n' +
  28. `https://github.com/electron-userland/electron-packager/blob/master/docs/api.md#${hash}\n`
  29. }
  30. function getVersion (opts, electronProp) {
  31. // Destructured assignments are added in Node 6
  32. const splitProp = electronProp.prop.split('.')
  33. const depType = splitProp[0]
  34. const packageName = splitProp[1]
  35. const src = electronProp.src
  36. if (packageName === 'electron-prebuilt-compile') {
  37. // electron-prebuilt-compile cannot be resolved because `main` does not point
  38. // to a valid JS file.
  39. const electronVersion = electronProp.pkg[depType][packageName]
  40. if (!/^\d+\.\d+\.\d+/.test(electronVersion)) {
  41. throw new Error('Using electron-prebuilt-compile with Electron Packager requires specifying an exact Electron version')
  42. }
  43. opts.electronVersion = electronVersion
  44. return Promise.resolve()
  45. } else {
  46. return pify(resolve, { multiArgs: true })(packageName, { basedir: path.dirname(src) })
  47. .then(res => {
  48. debug(`Inferring target Electron version from ${packageName} in ${src}`)
  49. const pkg = res[1]
  50. opts.electronVersion = pkg.version
  51. return null
  52. })
  53. }
  54. }
  55. function handleMetadata (opts, result) {
  56. if (result.values.productName) {
  57. debug(`Inferring application name from ${result.source.productName.prop} in ${result.source.productName.src}`)
  58. opts.name = result.values.productName
  59. }
  60. if (result.values.version) {
  61. debug(`Inferring appVersion from version in ${result.source.version.src}`)
  62. opts.appVersion = result.values.version
  63. }
  64. if (result.values.author && !opts.win32metadata) {
  65. opts.win32metadata = {}
  66. }
  67. if (result.values.author) {
  68. debug(`Inferring win32metadata.CompanyName from author in ${result.source.author.src}`)
  69. if (typeof result.values.author === 'string') {
  70. opts.win32metadata.CompanyName = parseAuthor(result.values.author).name
  71. } else if (result.values.author.name) {
  72. opts.win32metadata.CompanyName = result.values.author.name
  73. } else {
  74. debug('Cannot infer win32metadata.CompanyName from author, no name found')
  75. }
  76. }
  77. if (result.values['dependencies.electron']) {
  78. return getVersion(opts, result.source['dependencies.electron'])
  79. } else {
  80. return Promise.resolve()
  81. }
  82. }
  83. module.exports = function getMetadataFromPackageJSON (platforms, opts, dir) {
  84. let props = []
  85. if (!opts.name) props.push(['productName', 'name'])
  86. if (!opts.appVersion) props.push('version')
  87. if (!opts.electronVersion) {
  88. props.push([
  89. 'dependencies.electron',
  90. 'devDependencies.electron',
  91. 'dependencies.electron-prebuilt-compile',
  92. 'devDependencies.electron-prebuilt-compile',
  93. 'dependencies.electron-prebuilt',
  94. 'devDependencies.electron-prebuilt'
  95. ])
  96. }
  97. if (platforms.indexOf('win32') !== -1 && !(opts.win32metadata && opts.win32metadata.CompanyName)) {
  98. props.push('author')
  99. }
  100. // Name and version provided, no need to infer
  101. if (props.length === 0) return Promise.resolve()
  102. // Search package.json files to infer name and version from
  103. return pify(getPackageInfo)(props, dir)
  104. .then(result => handleMetadata(opts, result))
  105. .catch(err => {
  106. if (err.missingProps) {
  107. const missingProps = err.missingProps.map(prop => {
  108. return Array.isArray(prop) ? prop[0] : prop
  109. })
  110. if (isMissingRequiredProperty(missingProps)) {
  111. const messages = missingProps.map(errorMessageForProperty)
  112. debug(err.message)
  113. err.message = messages.join('\n') + '\n'
  114. throw err
  115. } else {
  116. // Missing props not required, can continue w/ partial result
  117. return handleMetadata(opts, err.result)
  118. }
  119. }
  120. throw err
  121. })
  122. }