napi.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. 'use strict';
  2. const fs = require('fs');
  3. module.exports = exports;
  4. const versionArray = process.version
  5. .substr(1)
  6. .replace(/-.*$/, '')
  7. .split('.')
  8. .map((item) => {
  9. return +item;
  10. });
  11. const napi_multiple_commands = [
  12. 'build',
  13. 'clean',
  14. 'configure',
  15. 'package',
  16. 'publish',
  17. 'reveal',
  18. 'testbinary',
  19. 'testpackage',
  20. 'unpublish'
  21. ];
  22. const napi_build_version_tag = 'napi_build_version=';
  23. module.exports.get_napi_version = function() {
  24. // returns the non-zero numeric napi version or undefined if napi is not supported.
  25. // correctly supporting target requires an updated cross-walk
  26. let version = process.versions.napi; // can be undefined
  27. if (!version) { // this code should never need to be updated
  28. if (versionArray[0] === 9 && versionArray[1] >= 3) version = 2; // 9.3.0+
  29. else if (versionArray[0] === 8) version = 1; // 8.0.0+
  30. }
  31. return version;
  32. };
  33. module.exports.get_napi_version_as_string = function(target) {
  34. // returns the napi version as a string or an empty string if napi is not supported.
  35. const version = module.exports.get_napi_version(target);
  36. return version ? '' + version : '';
  37. };
  38. module.exports.validate_package_json = function(package_json, opts) { // throws Error
  39. const binary = package_json.binary;
  40. const module_path_ok = pathOK(binary.module_path);
  41. const remote_path_ok = pathOK(binary.remote_path);
  42. const package_name_ok = pathOK(binary.package_name);
  43. const napi_build_versions = module.exports.get_napi_build_versions(package_json, opts, true);
  44. const napi_build_versions_raw = module.exports.get_napi_build_versions_raw(package_json);
  45. if (napi_build_versions) {
  46. napi_build_versions.forEach((napi_build_version)=> {
  47. if (!(parseInt(napi_build_version, 10) === napi_build_version && napi_build_version > 0)) {
  48. throw new Error('All values specified in napi_versions must be positive integers.');
  49. }
  50. });
  51. }
  52. if (napi_build_versions && (!module_path_ok || (!remote_path_ok && !package_name_ok))) {
  53. throw new Error('When napi_versions is specified; module_path and either remote_path or ' +
  54. "package_name must contain the substitution string '{napi_build_version}`.");
  55. }
  56. if ((module_path_ok || remote_path_ok || package_name_ok) && !napi_build_versions_raw) {
  57. throw new Error("When the substitution string '{napi_build_version}` is specified in " +
  58. 'module_path, remote_path, or package_name; napi_versions must also be specified.');
  59. }
  60. if (napi_build_versions && !module.exports.get_best_napi_build_version(package_json, opts) &&
  61. module.exports.build_napi_only(package_json)) {
  62. throw new Error(
  63. 'The Node-API version of this Node instance is ' + module.exports.get_napi_version(opts ? opts.target : undefined) + '. ' +
  64. 'This module supports Node-API version(s) ' + module.exports.get_napi_build_versions_raw(package_json) + '. ' +
  65. 'This Node instance cannot run this module.');
  66. }
  67. if (napi_build_versions_raw && !napi_build_versions && module.exports.build_napi_only(package_json)) {
  68. throw new Error(
  69. 'The Node-API version of this Node instance is ' + module.exports.get_napi_version(opts ? opts.target : undefined) + '. ' +
  70. 'This module supports Node-API version(s) ' + module.exports.get_napi_build_versions_raw(package_json) + '. ' +
  71. 'This Node instance cannot run this module.');
  72. }
  73. };
  74. function pathOK(path) {
  75. return path && (path.indexOf('{napi_build_version}') !== -1 || path.indexOf('{node_napi_label}') !== -1);
  76. }
  77. module.exports.expand_commands = function(package_json, opts, commands) {
  78. const expanded_commands = [];
  79. const napi_build_versions = module.exports.get_napi_build_versions(package_json, opts);
  80. commands.forEach((command)=> {
  81. if (napi_build_versions && command.name === 'install') {
  82. const napi_build_version = module.exports.get_best_napi_build_version(package_json, opts);
  83. const args = napi_build_version ? [napi_build_version_tag + napi_build_version] : [];
  84. expanded_commands.push({ name: command.name, args: args });
  85. } else if (napi_build_versions && napi_multiple_commands.indexOf(command.name) !== -1) {
  86. napi_build_versions.forEach((napi_build_version)=> {
  87. const args = command.args.slice();
  88. args.push(napi_build_version_tag + napi_build_version);
  89. expanded_commands.push({ name: command.name, args: args });
  90. });
  91. } else {
  92. expanded_commands.push(command);
  93. }
  94. });
  95. return expanded_commands;
  96. };
  97. module.exports.get_napi_build_versions = function(package_json, opts, warnings) { // opts may be undefined
  98. const log = require('npmlog');
  99. let napi_build_versions = [];
  100. const supported_napi_version = module.exports.get_napi_version(opts ? opts.target : undefined);
  101. // remove duplicates, verify each napi version can actaully be built
  102. if (package_json.binary && package_json.binary.napi_versions) {
  103. package_json.binary.napi_versions.forEach((napi_version) => {
  104. const duplicated = napi_build_versions.indexOf(napi_version) !== -1;
  105. if (!duplicated && supported_napi_version && napi_version <= supported_napi_version) {
  106. napi_build_versions.push(napi_version);
  107. } else if (warnings && !duplicated && supported_napi_version) {
  108. log.info('This Node instance does not support builds for Node-API version', napi_version);
  109. }
  110. });
  111. }
  112. if (opts && opts['build-latest-napi-version-only']) {
  113. let latest_version = 0;
  114. napi_build_versions.forEach((napi_version) => {
  115. if (napi_version > latest_version) latest_version = napi_version;
  116. });
  117. napi_build_versions = latest_version ? [latest_version] : [];
  118. }
  119. return napi_build_versions.length ? napi_build_versions : undefined;
  120. };
  121. module.exports.get_napi_build_versions_raw = function(package_json) {
  122. const napi_build_versions = [];
  123. // remove duplicates
  124. if (package_json.binary && package_json.binary.napi_versions) {
  125. package_json.binary.napi_versions.forEach((napi_version) => {
  126. if (napi_build_versions.indexOf(napi_version) === -1) {
  127. napi_build_versions.push(napi_version);
  128. }
  129. });
  130. }
  131. return napi_build_versions.length ? napi_build_versions : undefined;
  132. };
  133. module.exports.get_command_arg = function(napi_build_version) {
  134. return napi_build_version_tag + napi_build_version;
  135. };
  136. module.exports.get_napi_build_version_from_command_args = function(command_args) {
  137. for (let i = 0; i < command_args.length; i++) {
  138. const arg = command_args[i];
  139. if (arg.indexOf(napi_build_version_tag) === 0) {
  140. return parseInt(arg.substr(napi_build_version_tag.length), 10);
  141. }
  142. }
  143. return undefined;
  144. };
  145. module.exports.swap_build_dir_out = function(napi_build_version) {
  146. if (napi_build_version) {
  147. const rm = require('rimraf');
  148. rm.sync(module.exports.get_build_dir(napi_build_version));
  149. fs.renameSync('build', module.exports.get_build_dir(napi_build_version));
  150. }
  151. };
  152. module.exports.swap_build_dir_in = function(napi_build_version) {
  153. if (napi_build_version) {
  154. const rm = require('rimraf');
  155. rm.sync('build');
  156. fs.renameSync(module.exports.get_build_dir(napi_build_version), 'build');
  157. }
  158. };
  159. module.exports.get_build_dir = function(napi_build_version) {
  160. return 'build-tmp-napi-v' + napi_build_version;
  161. };
  162. module.exports.get_best_napi_build_version = function(package_json, opts) {
  163. let best_napi_build_version = 0;
  164. const napi_build_versions = module.exports.get_napi_build_versions(package_json, opts);
  165. if (napi_build_versions) {
  166. const our_napi_version = module.exports.get_napi_version(opts ? opts.target : undefined);
  167. napi_build_versions.forEach((napi_build_version)=> {
  168. if (napi_build_version > best_napi_build_version &&
  169. napi_build_version <= our_napi_version) {
  170. best_napi_build_version = napi_build_version;
  171. }
  172. });
  173. }
  174. return best_napi_build_version === 0 ? undefined : best_napi_build_version;
  175. };
  176. module.exports.build_napi_only = function(package_json) {
  177. return package_json.binary && package_json.binary.package_name &&
  178. package_json.binary.package_name.indexOf('{node_napi_label}') === -1;
  179. };