UNPKG

15.2 kBJavaScriptView Raw
1"use strict";
2var __assign = (this && this.__assign) || function () {
3 __assign = Object.assign || function(t) {
4 for (var s, i = 1, n = arguments.length; i < n; i++) {
5 s = arguments[i];
6 for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7 t[p] = s[p];
8 }
9 return t;
10 };
11 return __assign.apply(this, arguments);
12};
13var __importDefault = (this && this.__importDefault) || function (mod) {
14 return (mod && mod.__esModule) ? mod : { "default": mod };
15};
16Object.defineProperty(exports, "__esModule", { value: true });
17exports.requirePackage = exports.solicitEdition = exports.determineEdition = exports.isCompatibleEdition = exports.isCompatibleEngines = exports.isCompatibleVersion = exports.isValidEdition = exports.loadEdition = void 0;
18// Imports
19var path_1 = require("path");
20var version_range_1 = __importDefault(require("version-range"));
21var util_js_1 = require("./util.js");
22var process_1 = require("process");
23var fs_1 = require("fs");
24/**
25 * Load the {@link Edition} with the loader.
26 * @returns The result of the loaded edition.
27 * @throws If failed to load, an error is thrown with the reason.
28 */
29function loadEdition(edition, opts) {
30 var entry = path_1.resolve(opts.cwd || '', edition.directory, opts.entry || edition.entry || '');
31 if (opts.loader == null) {
32 throw util_js_1.errtion({
33 message: "Could not load the edition [" + edition.description + "] as no loader was specified. This is probably due to a testing misconfiguration.",
34 code: 'editions-autoloader-loader-missing',
35 level: 'fatal',
36 });
37 }
38 try {
39 return opts.loader.call(edition, entry);
40 }
41 catch (loadError) {
42 // Note the error with more details
43 throw util_js_1.errtion({
44 message: "Failed to load the entry [" + entry + "] of edition [" + edition.description + "].",
45 code: 'editions-autoloader-loader-failed',
46 level: 'fatal',
47 }, loadError);
48 }
49}
50exports.loadEdition = loadEdition;
51/**
52 * Verify the {@link Edition} has all the required properties.
53 * @returns if valid
54 * @throws if invalid
55 */
56function isValidEdition(edition) {
57 if (!edition.description ||
58 !edition.directory ||
59 !edition.entry ||
60 edition.engines == null) {
61 throw util_js_1.errtion({
62 message: "An edition must have its [description, directory, entry, engines] fields defined, yet all this edition defined were [" + Object.keys(edition).join(', ') + "]",
63 code: 'editions-autoloader-invalid-edition',
64 level: 'fatal',
65 });
66 }
67 // valid
68 return true;
69}
70exports.isValidEdition = isValidEdition;
71/**
72 * Is this {@link Edition} suitable for these versions?
73 * @returns if compatible
74 * @throws if incompatible
75 */
76function isCompatibleVersion(range, version, opts) {
77 // prepare
78 var broadenRange = opts.broadenRange;
79 if (!version)
80 throw util_js_1.errtion({
81 message: "No version was specified to compare the range [" + range + "] against",
82 code: 'editions-autoloader-engine-version-missing',
83 level: 'fatal',
84 });
85 if (range == null || range === '')
86 throw util_js_1.errtion({
87 message: "The edition range was not specified, so unable to compare against the version [" + version + "]",
88 code: 'editions-autoloader-engine-range-missing',
89 });
90 if (range === false)
91 throw util_js_1.errtion({
92 message: "The edition range does not support this engine",
93 code: 'editions-autoloader-engine-unsupported',
94 });
95 if (range === true)
96 return true;
97 // original range
98 try {
99 if (version_range_1.default(version, range))
100 return true;
101 }
102 catch (error) {
103 throw util_js_1.errtion({
104 message: "The range [" + range + "] was invalid, something is wrong with the Editions definition.",
105 code: 'editions-autoloader-invalid-range',
106 level: 'fatal',
107 }, error);
108 }
109 // broadened range
110 // https://github.com/bevry/editions/blob/master/HISTORY.md#v210-2018-november-15
111 // If none of the editions for a package match the current node version, editions will try to find a compatible package by converting strict version ranges likes 4 || 6 || 8 || 10 to looser ones like >=4, and if that fails, then it will attempt to load the last edition for the environment.
112 // This brings editions handling of engines closer in line with how node handles it, which is as a warning/recommendation, rather than a requirement/enforcement.
113 // This has the benefit that edition authors can specify ranges as the specific versions that they have tested the edition against that pass, rather than having to omit that information for runtime compatibility.
114 // As such editions will now automatically select the edition with guaranteed support for the environment, and if there are none with guaranteed support, then editions will select the one is most likely supported, and if there are none that are likely supported, then it will try the last edition, which should be the most compatible edition.
115 // This is timely, as node v11 is now the version most developers use, yet if edition authors specified only LTS releases, then the editions autoloader would reject loading on v11, despite compatibility being likely with the most upper edition.
116 // NOTE: That there is only one broadening chance per package, once a broadened edition has been returned, a load will be attempted, and if it fails, then the package failed. This is intentional.
117 if (broadenRange === true) {
118 // check if range can be broadened, validate it and extract
119 var broadenedRangeRegex = /^\s*([0-9.]+)\s*(\|\|\s*[0-9.]+\s*)*$/;
120 var broadenedRangeMatch = range.match(broadenedRangeRegex);
121 var lowestVersion = (broadenedRangeMatch && broadenedRangeMatch[1]) || '';
122 // ^ can't do number conversion, as 1.1.1 is not a number
123 // this also converts 0 to '' which is what we want for the next check
124 // confirm the validation
125 if (lowestVersion === '')
126 throw util_js_1.errtion({
127 message: "The range [" + range + "] is not able to be broadened, only ranges in format of [lowest] or [lowest || ... || ... ] can be broadened. Update the Editions definition and try again.",
128 code: 'editions-autoloader-unsupported-broadened-range',
129 level: 'fatal',
130 });
131 // create the broadened range, and attempt that
132 var broadenedRange = ">= " + lowestVersion;
133 try {
134 if (version_range_1.default(version, broadenedRange))
135 return true;
136 }
137 catch (error) {
138 throw util_js_1.errtion({
139 message: "The broadened range [" + broadenedRange + "] was invalid, something is wrong within Editions.",
140 code: 'editions-autoloader-invalid-broadened-range',
141 level: 'fatal',
142 }, error);
143 }
144 // broadened range was incompatible
145 throw util_js_1.errtion({
146 message: "The edition range [" + range + "] does not support this engine version [" + version + "], even when broadened to [" + broadenedRange + "]",
147 code: 'editions-autoloader-engine-incompatible-broadened-range',
148 });
149 }
150 // give up
151 throw util_js_1.errtion({
152 message: "The edition range [" + range + "] does not support this engine version [" + version + "]",
153 code: 'editions-autoloader-engine-incompatible-original',
154 });
155}
156exports.isCompatibleVersion = isCompatibleVersion;
157/**
158 * Checks that the provided engines are compatible against the provided versions.
159 * @returns if compatible
160 * @throws if incompatible
161 */
162function isCompatibleEngines(engines, opts) {
163 // PRepare
164 var versions = opts.versions;
165 // Check engines exist
166 if (!engines) {
167 throw util_js_1.errtion({
168 message: "The edition had no engines to compare against the environment",
169 code: 'editions-autoloader-invalid-engines',
170 });
171 }
172 // Check versions exist
173 if (!versions) {
174 throw util_js_1.errtion({
175 message: "No versions were supplied to compare the engines against",
176 code: 'editions-autoloader-invalid-versions',
177 level: 'fatal',
178 });
179 }
180 // Check each version
181 var compatible = false;
182 for (var key in engines) {
183 if (engines.hasOwnProperty(key)) {
184 // deno's std/node/process provides both `deno` and `node` keys
185 // so we don't won't to compare node when it is actually deno
186 if (key === 'node' && versions.deno)
187 continue;
188 // prepare
189 var engine = engines[key];
190 var version = versions[key];
191 // skip for engines this edition does not care about
192 if (version == null)
193 continue;
194 // check compatibility against all the provided engines it does care about
195 try {
196 isCompatibleVersion(engine, version, opts);
197 compatible = true;
198 // if any incompatibility, it is thrown, so no need to set to false
199 }
200 catch (rangeError) {
201 throw util_js_1.errtion({
202 message: "The engine [" + key + "] range of [" + engine + "] was not compatible against version [" + version + "].",
203 code: 'editions-autoloader-engine-error',
204 }, rangeError);
205 }
206 }
207 }
208 // if there were no matching engines, then throw
209 if (!compatible) {
210 throw util_js_1.errtion({
211 message: "There were no supported engines in which this environment provides.",
212 code: 'editions-autoloader-engine-mismatch',
213 });
214 }
215 // valid
216 return true;
217}
218exports.isCompatibleEngines = isCompatibleEngines;
219/**
220 * Checks that the {@link Edition} is compatible against the provided versions.
221 * @returns if compatible
222 * @throws if incompatible
223 */
224function isCompatibleEdition(edition, opts) {
225 try {
226 return isCompatibleEngines(edition.engines, opts);
227 }
228 catch (compatibleError) {
229 throw util_js_1.errtion({
230 message: "The edition [" + edition.description + "] is not compatible with this environment.",
231 code: 'editions-autoloader-edition-incompatible',
232 }, compatibleError);
233 }
234}
235exports.isCompatibleEdition = isCompatibleEdition;
236/**
237 * Determine which edition should be loaded.
238 * If {@link VersionOptions.broadenRange} is unspecified (the default behavior), then we attempt to determine a suitable edition without broadening the range, and if that fails, then we try again with the range broadened.
239 * @returns any suitable editions
240 * @throws if no suitable editions
241 */
242function determineEdition(editions, opts) {
243 // Prepare
244 var broadenRange = opts.broadenRange;
245 // Check
246 if (!editions || editions.length === 0) {
247 throw util_js_1.errtion({
248 message: 'No editions were specified.',
249 code: 'editions-autoloader-editions-missing',
250 });
251 }
252 // Cycle through the editions determining the above
253 var failure = null;
254 for (var i = 0; i < editions.length; ++i) {
255 var edition = editions[i];
256 try {
257 isValidEdition(edition);
258 isCompatibleEdition(edition, opts);
259 // Success! Return the edition
260 return edition;
261 }
262 catch (error) {
263 if (error.level === 'fatal') {
264 throw util_js_1.errtion({
265 message: "Unable to determine a suitable edition due to failure.",
266 code: 'editions-autoloader-fatal',
267 level: 'fatal',
268 }, error);
269 }
270 else if (failure) {
271 failure = util_js_1.errtion(error, failure);
272 }
273 else {
274 failure = error;
275 }
276 }
277 }
278 // Report the failure from above
279 if (failure) {
280 // try broadened
281 if (broadenRange == null)
282 try {
283 // return if broadening successfully returned an edition
284 var broadenedEdition = determineEdition(editions, __assign(__assign({}, opts), { broadenRange: true }));
285 return __assign(__assign({}, broadenedEdition), {
286 // bubble the circumstances up in case the loading of the broadened edition fails and needs to be reported
287 debugging: util_js_1.errtion({
288 message: "The edition " + broadenedEdition.description + " was selected to be force loaded as its range was broadened.",
289 code: 'editions-autoloader-attempt-broadened',
290 }) });
291 }
292 catch (error) {
293 throw util_js_1.errtion({
294 message: "Unable to determine a suitable edition, even after broadening.",
295 code: 'editions-autoloader-none-broadened',
296 }, error);
297 }
298 // fail
299 throw util_js_1.errtion({
300 message: "Unable to determine a suitable edition, as none were suitable.",
301 code: 'editions-autoloader-none-suitable',
302 }, failure);
303 }
304 // this should never reach here
305 throw util_js_1.errtion({
306 message: "Unable to determine a suitable edition, as an unexpected pathway occurred.",
307 code: 'editions-autoloader-never',
308 });
309}
310exports.determineEdition = determineEdition;
311/**
312 * Determine which edition should be loaded, and attempt to load it.
313 * @returns the loaded result of the suitable edition
314 * @throws if no suitable editions, or the edition failed to load
315 */
316function solicitEdition(editions, opts) {
317 var edition = determineEdition(editions, opts);
318 try {
319 return loadEdition(edition, opts);
320 }
321 catch (error) {
322 throw util_js_1.errtion(error, edition.debugging);
323 }
324}
325exports.solicitEdition = solicitEdition;
326/**
327 * Cycle through the editions for a package, determine the compatible edition, and load it.
328 * @returns the loaded result of the suitable edition
329 * @throws if no suitable editions, or if the edition failed to load
330 */
331function requirePackage(cwd, loader, entry) {
332 var packagePath = path_1.resolve(cwd || '', 'package.json');
333 try {
334 // load editions
335 var editions = JSON.parse(fs_1.readFileSync(packagePath, 'utf8')).editions;
336 // load edition
337 return solicitEdition(editions, {
338 versions: process_1.versions,
339 cwd: cwd,
340 loader: loader,
341 entry: entry,
342 });
343 }
344 catch (error) {
345 throw util_js_1.errtion({
346 message: "Unable to determine a suitable edition for the package [" + packagePath + "] and entry [" + entry + "]",
347 code: 'editions-autoloader-package',
348 }, error);
349 }
350}
351exports.requirePackage = requirePackage;