index.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. /* eslint no-console:0 */
  2. 'use strict';
  3. var __assign = (this && this.__assign) || function () {
  4. __assign = Object.assign || function(t) {
  5. for (var s, i = 1, n = arguments.length; i < n; i++) {
  6. s = arguments[i];
  7. for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
  8. t[p] = s[p];
  9. }
  10. return t;
  11. };
  12. return __assign.apply(this, arguments);
  13. };
  14. var __importDefault = (this && this.__importDefault) || function (mod) {
  15. return (mod && mod.__esModule) ? mod : { "default": mod };
  16. };
  17. var _a, _b;
  18. Object.defineProperty(exports, "__esModule", { value: true });
  19. // Imports
  20. var path_1 = __importDefault(require("path"));
  21. var semver_1 = __importDefault(require("semver"));
  22. var util_js_1 = require("./util.js");
  23. // As Errlop uses Editions, we should use a specific Errlop edition
  24. // As otherwise, the circular reference may fail on some machines
  25. // https://github.com/bevry/errlop/issues/2
  26. var errlop_1 = __importDefault(require("errlop"));
  27. /**
  28. * The current node version that we are operating within.
  29. * It is compared in {@link requireEdition} against {@link Edition.engines}.
  30. */
  31. var NODE_VERSION = process.versions.node;
  32. /**
  33. * Whether or not {@link EDITIONS_VERBOSE} is enabled.
  34. * @type {bollean}
  35. * @private
  36. */
  37. var VERBOSE = process.env.EDITIONS_VERBOSE === true ||
  38. process.env.EDITIONS_VERBOSE === 'yes' ||
  39. process.env.EDITIONS_VERBOSE === 'true' ||
  40. false;
  41. /**
  42. * A list of the blacklisted tags.
  43. * Data imported from {@link EDITIONS_TAG_BLACKLIST}.
  44. */
  45. var BLACKLIST = ((_a = process.env.EDITIONS_TAG_BLACKLIST) === null || _a === void 0 ? void 0 : _a.split(/[, ]+/g)) || ((_b = process.env.EDITIONS_SYNTAX_BLACKLIST) === null || _b === void 0 ? void 0 : _b.split(/[, ]+/g));
  46. /**
  47. * A mapping of blacklisted tags to their reasons.
  48. * Keys are the tags.
  49. * Values are the error instances that contain the reasoning for why/how that tag is/became blacklisted.
  50. * Data imported from {@link EDITIONS_TAG_BLACKLIST}.
  51. */
  52. var blacklist = {};
  53. // Create the mapping of blacklisted tags and their reasonings
  54. if (BLACKLIST) {
  55. for (var i = 0; i < BLACKLIST.length; ++i) {
  56. var tag = BLACKLIST[i].trim().toLowerCase();
  57. blacklist[tag] = util_js_1.errtion({
  58. message: "The EDITIONS_TAG_BLACKLIST (aka EDITIONS_SYNTAX_BLACKLIST) environment variable blacklisted the tag [" + tag + "]",
  59. code: 'blacklisted-tag'
  60. });
  61. }
  62. }
  63. // Blacklist the tag 'esnext' if our node version is below 0.12
  64. if (semver_1.default.satisfies(NODE_VERSION, '<0.12')) {
  65. blacklist.esnext = new Error('The esnext tag is skipped on early node versions as attempting to use esnext features will output debugging information on these node versions');
  66. }
  67. /**
  68. * Attempt to load a specific {@link Edition}.
  69. * @returns The result of the loaded edition.
  70. * @throws An error if the edition failed to load.
  71. */
  72. function loadEdition(edition, opts) {
  73. var entry = path_1.default.resolve(opts.cwd || '', edition.directory, opts.entry || edition.entry || '');
  74. if (opts.require == null) {
  75. throw util_js_1.errtion({
  76. message: "Skipped edition [" + edition.description + "] as opts.require was not provided, this is probably due to a testing misconfiguration.",
  77. code: 'unsupported-edition-require'
  78. });
  79. }
  80. try {
  81. return opts.require(entry);
  82. }
  83. catch (loadError) {
  84. // Note the error with more details
  85. throw util_js_1.errtion({
  86. message: "Skipped edition [" + edition.description + "] at entry [" + entry + "] because it failed to load",
  87. code: 'unsupported-edition-tried'
  88. }, loadError);
  89. }
  90. }
  91. exports.loadEdition = loadEdition;
  92. /**
  93. * Attempt to require an {@link Edition}, based on its compatibility with the current environment, such as {@link NODE_VERSION} and {@link EDITIONS_TAG_BLACKLIST} compatibility.
  94. * If compatibility is established with the environment, it will load the edition using {@link loadEdition}.
  95. * @returns The result of the loaded edition
  96. * @throws An error if the edition failed to load
  97. */
  98. function requireEdition(edition, opts) {
  99. // Verify the edition is valid
  100. if (!edition.description ||
  101. !edition.directory ||
  102. !edition.entry ||
  103. edition.engines == null) {
  104. throw util_js_1.errtion({
  105. message: "Each edition must have its [description, directory, entry, engines] fields defined, yet all it had was [" + Object.keys(edition).join(', ') + "]",
  106. code: 'unsupported-edition-malformed',
  107. level: 'fatal'
  108. });
  109. }
  110. // Handle strict omission
  111. if (opts.strict == null) {
  112. try {
  113. return requireEdition(edition, __assign(__assign({}, opts), { strict: true }));
  114. }
  115. catch (err) {
  116. return requireEdition(edition, __assign(__assign({}, opts), { strict: false }));
  117. }
  118. }
  119. // Verify tag support
  120. // Convert tags into a sorted lowercase string
  121. var tags = (edition.tags || edition.syntaxes || [])
  122. .map(function (i) { return i.toLowerCase(); })
  123. .sort();
  124. for (var index = 0; index < tags.length; index++) {
  125. var tag = tags[index];
  126. var blacklisted = blacklist[tag];
  127. if (blacklisted) {
  128. throw util_js_1.errtion({
  129. message: "Skipping edition [" + edition.description + "] because it contained a blacklisted tag [" + tag + "]",
  130. code: 'unsupported-edition-backlisted-tag'
  131. }, blacklisted);
  132. }
  133. }
  134. // Verify engine support
  135. if (edition.engines === false) {
  136. throw util_js_1.errtion({
  137. message: "Skipping edition [" + edition.description + "] because its engines field was false",
  138. code: 'unsupported-edition-engine'
  139. });
  140. }
  141. if (!edition.engines.node) {
  142. throw util_js_1.errtion({
  143. message: "Skipping edition [" + edition.description + "] because its .engines.node field was falsey",
  144. code: 'unsupported-edition-engines-node'
  145. });
  146. }
  147. if (opts.strict) {
  148. if (edition.engines.node === true) {
  149. throw util_js_1.errtion({
  150. message: "Skipping edition [" + edition.description + "] because its .engines.node field was true yet we are in strict mode",
  151. code: 'unsupported-edition-engines-node-version-true'
  152. });
  153. }
  154. else if (semver_1.default.satisfies(NODE_VERSION, edition.engines.node) === false) {
  155. throw util_js_1.errtion({
  156. message: "Skipping edition [" + edition.description + "] because our current node version [" + NODE_VERSION + "] is not supported by its specific range [" + util_js_1.stringify(edition.engines.node) + "]",
  157. code: 'unsupported-edition-engines-node-version-specific'
  158. });
  159. }
  160. }
  161. else if (edition.engines.node !== true) {
  162. var simplifiedRange = util_js_1.simplifyRange(edition.engines.node);
  163. if (semver_1.default.satisfies(NODE_VERSION, simplifiedRange) === false) {
  164. throw util_js_1.errtion({
  165. message: "Skipping edition [" + edition.description + "] because our current node version [" + NODE_VERSION + "] is not supported by its simplified range [" + util_js_1.stringify(simplifiedRange) + "]",
  166. code: 'unsupported-edition-engines-node-version-simplified'
  167. });
  168. }
  169. }
  170. // Load the edition
  171. return loadEdition(edition, opts);
  172. }
  173. exports.requireEdition = requireEdition;
  174. /**
  175. * Cycles through a list of editions, returning the require result of the first suitable {@link Edition} that it was able to load.
  176. * Editions should be ordered from most preferable first, to least desirable last.
  177. * Providing the editions configuration is valid, individual edition handling is forwarded to {@link requireEdition}.
  178. * @returns The result of the loaded edition.
  179. * @throws An error if a suitable edition was unable to be resolved.
  180. */
  181. function requireEditions(editions, opts) {
  182. // Check
  183. if (!editions || editions.length === 0) {
  184. if (opts.packagePath) {
  185. throw util_js_1.errtion({
  186. message: "There were no editions specified for package [" + opts.packagePath + "]",
  187. code: 'unsupported-editions-missing'
  188. });
  189. }
  190. else {
  191. throw util_js_1.errtion({
  192. message: 'There were no editions specified',
  193. code: 'unsupported-editions-missing'
  194. });
  195. }
  196. }
  197. // Handle strict omission
  198. if (opts.strict == null) {
  199. try {
  200. return requireEditions(editions, __assign(__assign({}, opts), { strict: true }));
  201. }
  202. catch (err) {
  203. return requireEditions(editions, __assign(__assign({}, opts), { strict: false }));
  204. }
  205. }
  206. // Whether or not we should be verbose
  207. var verbose = opts.verbose == null ? VERBOSE : opts.verbose;
  208. // Capture the load result, the last error, and the fallback option
  209. var result, loaded = false, editionsError = null, fallbackEdition = null;
  210. // Cycle through the editions determing the above
  211. for (var i = 0; i < editions.length; ++i) {
  212. var edition = editions[i];
  213. try {
  214. result = requireEdition(edition, opts);
  215. loaded = true;
  216. break;
  217. }
  218. catch (editionError) {
  219. if (editionError.level === 'fatal') {
  220. editionsError = editionError;
  221. break;
  222. }
  223. else if (editionsError) {
  224. editionsError = util_js_1.errtion(editionsError, editionError);
  225. }
  226. else {
  227. editionsError = editionError;
  228. }
  229. // make the fallback edition one that we don't bother loading due to its engines
  230. // also: don't assume that .code is accessible, as it may not be, even if it should be, due to the way different environments behave
  231. if (String(editionError.code || '').indexOf('unsupported-edition-engines-node-version') === 0) {
  232. fallbackEdition = edition;
  233. }
  234. }
  235. }
  236. // if no edition was suitable for our environment, then try the fallback if it exists
  237. // that is to say, ignore its engines.node
  238. if (opts.strict === false && loaded === false && fallbackEdition) {
  239. try {
  240. result = loadEdition(fallbackEdition, opts);
  241. loaded = true;
  242. }
  243. catch (editionError) {
  244. editionsError = new errlop_1.default(editionError, editionsError);
  245. }
  246. }
  247. // if we were able to load something, then provide it
  248. if (loaded) {
  249. // make note of any errors if desired
  250. if (editionsError && verbose) {
  251. var stderr = opts.stderr || process.stderr;
  252. stderr.write(editionsError.stack + '\n');
  253. }
  254. return result;
  255. }
  256. // otherwise, provide the error
  257. else if (editionsError) {
  258. if (opts.packagePath) {
  259. throw util_js_1.errtion({
  260. message: "There were no suitable editions for package [" + opts.packagePath + "]",
  261. code: 'unsupported-editions-tried'
  262. }, editionsError);
  263. }
  264. else {
  265. throw util_js_1.errtion({
  266. message: 'There were no suitable editions',
  267. code: 'unsupported-editions-tried'
  268. }, editionsError);
  269. }
  270. }
  271. }
  272. exports.requireEditions = requireEditions;
  273. /**
  274. * Cycle through the editions for a package and require the correct one.
  275. * Providing the package configuration is valid, editions handling is forwarded to {@link requireEditions}.
  276. * @returns The result of the loaded edition.
  277. * @throws An error if a suitable edition was unable to be resolved.
  278. */
  279. function requirePackage(cwd, require, entry) {
  280. // Load the package.json file to fetch `name` for debugging and `editions` for loading
  281. var packagePath = path_1.default.resolve(cwd || '', 'package.json');
  282. var editions = require(packagePath).editions;
  283. var opts = { packagePath: packagePath, cwd: cwd, require: require, entry: entry };
  284. return requireEditions(editions, opts);
  285. }
  286. exports.requirePackage = requirePackage;