resolve(context, path, request, resolveContext, callback) {
		if (!context || typeof context !== "object") {
			return callback(new Error("context argument is not an object"));
		}
		if (typeof path !== "string") {
			return callback(new Error("path argument is not a string"));
		}
		if (typeof request !== "string") {
			return callback(new Error("request argument is not a string"));
		}
		if (!resolveContext) {
			return callback(new Error("resolveContext argument is not set"));
		}

		/** @type {ResolveRequest} */
		const obj = {
			context,
			path,
			request,
		};

		/** @type {ResolveContextYield | undefined} */
		let yield_;
		let yieldCalled = false;
		/** @type {ResolveContextYield | undefined} */
		let finishYield;
		if (typeof resolveContext.yield === "function") {
			const old = resolveContext.yield;
			/**
			 * @param {ResolveRequest} obj object
			 */
			yield_ = (obj) => {
				old(obj);
				yieldCalled = true;
			};
			/**
			 * @param {ResolveRequest} result result
			 * @returns {void}
			 */
			finishYield = (result) => {
				if (result) {
					/** @type {ResolveContextYield} */ (yield_)(result);
				}
				callback(null);
			};
		}

		const message = `resolve '${request}' in '${path}'`;

		/**
		 * @param {ResolveRequest} result result
		 * @returns {void}
		 */
		const finishResolved = (result) =>
			callback(
				null,
				result.path === false
					? false
					: `${result.path.replace(/#/g, "\0#")}${
							result.query ? result.query.replace(/#/g, "\0#") : ""
						}${result.fragment || ""}`,
				result,
			);

		/**
		 * @param {string[]} log logs
		 * @returns {void}
		 */
		const finishWithoutResolve = (log) => {
			/**
			 * @type {ErrorWithDetail}
			 */
			const error = new Error(`Can't ${message}`);
			error.details = log.join("\n");
			this.hooks.noResolve.call(obj, error);
			return callback(error);
		};

		if (resolveContext.log) {
			// We need log anyway to capture it in case of an error
			const parentLog = resolveContext.log;
			/** @type {string[]} */
			const log = [];
			return this.doResolve(
				this.hooks.resolve,
				obj,
				message,
				{
					log: (msg) => {
						parentLog(msg);
						log.push(msg);
					},
					yield: yield_,
					fileDependencies: resolveContext.fileDependencies,
					contextDependencies: resolveContext.contextDependencies,
					missingDependencies: resolveContext.missingDependencies,
					stack: resolveContext.stack,
				},
				(err, result) => {
					if (err) return callback(err);

					if (yieldCalled || (result && yield_)) {
						return /** @type {ResolveContextYield} */ (finishYield)(
							/** @type {ResolveRequest} */ (result),
						);
					}

					if (result) return finishResolved(result);

					return finishWithoutResolve(log);
				},
			);
		}
		// Try to resolve assuming there is no error
		// We don't log stuff in this case
		return this.doResolve(
			this.hooks.resolve,
			obj,
			message,
			{
				log: undefined,
				yield: yield_,
				fileDependencies: resolveContext.fileDependencies,
				contextDependencies: resolveContext.contextDependencies,
				missingDependencies: resolveContext.missingDependencies,
				stack: resolveContext.stack,
			},
			(err, result) => {
				if (err) return callback(err);

				if (yieldCalled || (result && yield_)) {
					return /** @type {ResolveContextYield} */ (finishYield)(
						/** @type {ResolveRequest} */ (result),
					);
				}

				if (result) return finishResolved(result);

				// log is missing for the error details
				// so we redo the resolving for the log info
				// this is more expensive to the success case
				// is assumed by default
				/** @type {string[]} */
				const log = [];

				return this.doResolve(
					this.hooks.resolve,
					obj,
					message,
					{
						log: (msg) => log.push(msg),
						yield: yield_,
						stack: resolveContext.stack,
					},
					(err, result) => {
						if (err) return callback(err);

						// In a case that there is a race condition and yield will be called
						if (yieldCalled || (result && yield_)) {
							return /** @type {ResolveContextYield} */ (finishYield)(
								/** @type {ResolveRequest} */ (result),
							);
						}

						return finishWithoutResolve(log);
					},
				);
			},
		);
	}
@param {object} context context information object
@param {string} path context path
@param {string} request request string
@param {ResolveContext} resolveContext resolve context
@param {ResolveCallback} callback callback function
@returns {void}