UNPKG

disposablestack

Version:

An ESnext spec-compliant `DisposableStack`, `AsyncDisposableStack`, `Symbol.dispose`, and `Symbol.asyncDispose` shim/polyfill/replacement that works as far down as ES3.

101 lines (85 loc) 3.38 kB
'use strict'; var GetIntrinsic = require('get-intrinsic'); var $SyntaxError = require('es-errors/syntax'); var $TypeError = require('es-errors/type'); var $Promise = GetIntrinsic('%Promise%', true); var callBound = require('call-bound'); var $then = callBound('Promise.prototype.then', true); var CompletionRecord = require('es-abstract/2024/CompletionRecord'); var Dispose = require('./Dispose'); var NormalCompletion = require('es-abstract/2024/NormalCompletion'); var PromiseResolve = require('es-abstract/2024/PromiseResolve'); var ThrowCompletion = require('es-abstract/2024/ThrowCompletion'); var SuppressedError = require('suppressed-error/polyfill')(); module.exports = function DisposeResources(disposeCapability, completion) { // assertRecord('DisposeCapability Record', disposeCapability, 'disposeCapability'); ?? if (!(completion instanceof CompletionRecord)) { throw new $TypeError('`completion` must be a Completion Record'); } var stack = disposeCapability['[[DisposableResourceStack]]']; if (!stack) { throw new $TypeError('Assertion failed: `disposeCapability.[[DisposableResourceStack]]` must not be ~EMPTY~'); // step 1 } // for DisposableStack or AsyncDisposableStack, all are sync, or all are async. // Only an environment record, via `using` and `await using`, can mix sync and async. var actualHint; for (var j = stack.length - 1; j >= 0; j -= 1) { // assertRecord('DisposableResource Record', resource); ?? if (!actualHint) { actualHint = stack[j]['[[Hint]]']; } else if (actualHint !== stack[j]['[[Hint]]']) { throw new $SyntaxError('mixed hint stacks are not supported'); } } var promise = actualHint === 'ASYNC-DISPOSE' && PromiseResolve($Promise, completion); var rejecter = function (e) { if (completion.type() === 'throw') { // step 2.b.i var suppressed = completion.value(); // step 2.b.i.2 var error = new SuppressedError(e, suppressed); // steps 2.b.i.1, 2.b.i.3 - 2.b.i.5 // eslint-disable-next-line no-param-reassign completion = ThrowCompletion(error); // step 2.b.i.6 } else { // step 2.b.ii // eslint-disable-next-line no-param-reassign completion = ThrowCompletion(e); // step 2.b.ii.1 } }; var getPromise = actualHint === 'ASYNC-DISPOSE' && function getPromise(resource) { return $then( promise, function () { var result = Dispose( // step 2.a resource['[[ResourceValue]]'], resource['[[Hint]]'], resource['[[DisposeMethod]]'] ); if (!result) { throw new $SyntaxError('Assertion failed: non-`~ASYNC-DISPOSE~` resource returned a promise from Dispose'); } return $then(result, NormalCompletion); }, rejecter ); }; for (var i = stack.length - 1; i >= 0; i -= 1) { // step 2 if (actualHint === 'ASYNC-DISPOSE') { promise = getPromise(stack[i]); } else { var resource = stack[i]; try { var result = Dispose( // step 2.a resource['[[ResourceValue]]'], resource['[[Hint]]'], resource['[[DisposeMethod]]'] ); if (result) { throw new $SyntaxError('Assertion failed: `~SYNC-DISPOSE~` resource returned something from Dispose'); } } catch (e) { rejecter(e); } } } // eslint-disable-next-line no-param-reassign disposeCapability['[[DisposableResourceStack]]'] = null; // step 3 return actualHint === 'ASYNC-DISPOSE' ? promise : completion; // step 4 };