ronantakizawa/codereview-bench huggingface dataset:
Repository: sveltejs/svelte
File: packages/svelte/src/index-server.js (JavaScript)

Code before:
}

export function hydrate() {
	e.lifecycle_function_unavailable('hydrate');
}

export function unmount() {
	e.lifecycle_function_unavailable('unmount');
}

export async function tick() {}

/** @type {AbortController | null} */
let controller = null;

export function getAbortSignal() {
	if (controller === null) {
		const c = (controller = new AbortController());
		queueMicrotask(() => {
			c.abort(STALE_REACTION);
			controller = null;
		});
	}

	return controller.signal;
}

export { getAllContexts, getContext, hasContext, setContext } from './internal/server/context.js';

export { createRawSnippet } from './internal/server/blocks/snippet.js';


Review comment (suggestion):
I think this needs to change -- ideally this abort signal is aborted at the end of `render` rather than after a microtask, as a microtask will both be "too late" and also will interfere with future async SSR work

Code after:
/** @import { Component } from '#server' */
import { current_component } from './internal/server/context.js';
import { noop } from './internal/shared/utils.js';
import * as e from './internal/server/errors.js';

/** @param {() => void} fn */
export function onDestroy(fn) {
	var context = /** @type {Component} */ (current_component);
	(context.d ??= []).push(fn);
}

export {
	noop as beforeUpdate,
	noop as afterUpdate,
	noop as onMount,
	noop as flushSync,
	run as untrack
} from './internal/shared/utils.js';

export function createEventDispatcher() {
	return noop;
}

export function mount() {
	e.lifecycle_function_unavailable('mount');
}

export function hydrate() {
	e.lifecycle_function_unavailable('hydrate');
}

export function unmount() {
	e.lifecycle_function_unavailable('unmount');
}

export async function tick() {}

export { getAbortSignal } from './internal/server/abort-signal.js';

export { getAllContexts, getContext, hasContext, setContext } from './internal/server/context.js';

export { createRawSnippet } from './internal/server/blocks/snippet.js';
Repository: sveltejs/svelte
File: packages/svelte/src/internal/server/abort-signal.js (JavaScript)

Code before:
import { STALE_REACTION } from '#client/constants';

/** @type {AbortController | null} */
export let controller = null;

export function abort() {
	if (controller !== null) {
		controller.abort(STALE_REACTION);
		controller = null;
	}
}

export function getAbortSignal() {
	return (controller ??= new AbortController()).signal;
}


Review comment (suggestion):
```suggestion
	controller?.abort(STALE_REACTION);
	controller = null;
```

same here

Code after:
import { STALE_REACTION } from '#client/constants';

/** @type {AbortController | null} */
export let controller = null;

export function abort() {
	controller?.abort(STALE_REACTION);
	controller = null;
}

export function getAbortSignal() {
	return (controller ??= new AbortController()).signal;
}
Repository: sveltejs/svelte
File: packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js (JavaScript)

Code before:

				expressions.push(value);

				quasi = b.quasi('', i + 1 === values.length);
				quasis.push(quasi);
			}
		}
	}

	for (const quasi of quasis) {
		quasi.value.raw = sanitize_template_string(/** @type {string} */ (quasi.value.cooked));
	}

	const value =
		expressions.length > 0
			? b.template(quasis, expressions)
			: b.literal(/** @type {string} */ (quasi.value.cooked));

	return { value, has_state };
}

/**
 * @param {ComponentClientTransformState} state
 */
export function build_render_statement(state) {
	const { memoizer } = state;

	const ids = state.memoizer.apply();
	const values = memoizer.sync_values();

	return b.stmt(
		b.call(
			'$.template_effect',
			b.arrow(
				ids,
				state.update.length === 1 && state.update[0].type === 'ExpressionStatement'
					? state.update[0].expression
					: b.block(state.update)
			),
			values,
			values && !state.analysis.runes && b.id('$.derived_safe_equal')
		)
	);
}

/**
 * For unfortunate legacy reasons, directive names can look like this `use:a.b-c`
 * This turns that string into a member expression
 * @param {string} name
 */
export function parse_directive_name(name) {

Review comment (suggestion):
```suggestion
	const ids = memoizer.apply();
```

Code after:

				expressions.push(value);

				quasi = b.quasi('', i + 1 === values.length);
				quasis.push(quasi);
			}
		}
	}

	for (const quasi of quasis) {
		quasi.value.raw = sanitize_template_string(/** @type {string} */ (quasi.value.cooked));
	}

	const value =
		expressions.length > 0
			? b.template(quasis, expressions)
			: b.literal(/** @type {string} */ (quasi.value.cooked));

	return { value, has_state };
}

/**
 * @param {ComponentClientTransformState} state
 */
export function build_render_statement(state) {
	const ids = state.memoizer.apply();
	const values = state.memoizer.sync_values();

	return b.stmt(
		b.call(
			'$.template_effect',
			b.arrow(
				ids,
				state.update.length === 1 && state.update[0].type === 'ExpressionStatement'
					? state.update[0].expression
					: b.block(state.update)
			),
			values,
			values && !state.analysis.runes && b.id('$.derived_safe_equal')
		)
	);
}

/**
 * For unfortunate legacy reasons, directive names can look like this `use:a.b-c`
 * This turns that string into a member expression
 * @param {string} name
 */
export function parse_directive_name(name) {
	// this allow for accessing members of an object
	const parts = name.split('.');
Repository: sveltejs/svelte
File: packages/svelte/src/internal/client/reactivity/props.js (JavaScript)

Code before:

	// Capture the initial value if it's bindable
	if (bindable) get(d);

	return function (/** @type {any} */ value, /** @type {boolean} */ mutation) {
		if (arguments.length > 0) {
			const new_value = mutation ? get(d) : runes && bindable ? proxy(value) : value;

			set(d, new_value);

			if (fallback_value !== undefined) {
				fallback_value = new_value;
			}

			return value;
		}

		// TODO is this still necessary post-#16263?
		if (has_destroyed_component_ctx(d)) {
			return d.v;
		}

		return get(d);
	};
}


Review comment (bug):
Pretty sure yes because things not read in a teardown can still cause bugs as seen in https://github.com/sveltejs/svelte/issues/16072 - we might need to make props signals after all, gonna investigate that soon

Code after:

			return getter();
		};
	}

	// prop is written to, but there's no binding, which means we
	// create a derived that we can write to locally
	var d = ((flags & PROPS_IS_IMMUTABLE) !== 0 ? derived : derived_safe_equal)(getter);

	// Capture the initial value if it's bindable
	if (bindable) get(d);

	return function (/** @type {any} */ value, /** @type {boolean} */ mutation) {
		if (arguments.length > 0) {
			const new_value = mutation ? get(d) : runes && bindable ? proxy(value) : value;

			set(d, new_value);

			if (fallback_value !== undefined) {
				fallback_value = new_value;
			}

			return value;
		}

		// TODO is this still necessary post-#16263?
		if (has_destroyed_component_ctx(d)) {
			return d.v;
		}

		return get(d);
	};
}
Repository: sveltejs/svelte
File: packages/svelte/src/internal/client/dom/blocks/boundary.js (JavaScript)

Code before:
} from '../../runtime.js';
import {
	hydrate_next,
	hydrate_node,
	hydrating,
	next,
	remove_nodes,
	set_hydrate_node
} from '../hydration.js';
import { queue_micro_task } from '../task.js';

/**
 * @param {Effect} boundary
 * @param {() => void} fn
 */
function with_boundary(boundary, fn) {
	var previous_effect = active_effect;
	var previous_reaction = active_reaction;
	var previous_ctx = component_context;

	set_active_effect(boundary);
	set_active_reaction(boundary);
	set_component_context(boundary.ctx);

	try {
		fn();
	} finally {
		set_active_effect(previous_effect);
		set_active_reaction(previous_reaction);
		set_component_context(previous_ctx);
	}
}

/**
 * @param {TemplateNode} node
 * @param {{
 * 	 onerror?: (error: unknown, reset: () => void) => void,
 *   failed?: (anchor: Node, error: () => unknown, reset: () => () => void) => void
 * }} props
 * @param {((anchor: Node) => void)} boundary_fn
 * @returns {void}
 */
export function boundary(node, props, boundary_fn) {
	var anchor = node;

	/** @type {Effect} */
	var boundary_effect;

	block(() => {
		var boundary = /** @type {Effect} */ (active_effect);
		var hydrate_open = hydrate_node;

Review comment (refactor):
if this is intended to match the signature of the `Boundary` constructor you can just type it `/** @type {typeof Boundary} */` to avoid having all of the duplicate JSDoc

Code after:
import {
	hydrate_next,
	hydrate_node,
	hydrating,
	next,
	remove_nodes,
	set_hydrate_node
} from '../hydration.js';
import { queue_micro_task } from '../task.js';

/**
 * @typedef {{
 * 	 onerror?: (error: unknown, reset: () => void) => void;
 *   failed?: (anchor: Node, error: () => unknown, reset: () => () => void) => void;
 * }} BoundaryProps
 */

var flags = EFFECT_TRANSPARENT | EFFECT_PRESERVED | BOUNDARY_EFFECT;

/**
 * @param {TemplateNode} node
 * @param {BoundaryProps} props
 * @param {((anchor: Node) => void)} children
 * @returns {void}
 */
export function boundary(node, props, children) {
	new Boundary(node, props, children);
}

export class Boundary {
	/** @type {TemplateNode} */
	#anchor;

	/** @type {TemplateNode} */
	#hydrate_open;

	/** @type {BoundaryProps} */
	#props;

	/** @type {((anchor: Node) => void)} */
	#children;

	/** @type {Effect} */
	#effect;

	/** @type {Effect | null} */
	#main_effect = null;

	/** @type {Effect | null} */
	#failed_effect = null;
Repository: sveltejs/svelte
File: packages/svelte/src/compiler/phases/patterns.js (JavaScript)

Code before:
export const regex_whitespace = /\s/;
export const regex_whitespaces = /\s+/;
export const regex_starts_with_newline = /^\r?\n/;
export const regex_starts_with_whitespace = /^\s/;
export const regex_starts_with_whitespaces = /^[ \t\r\n]+/;
export const regex_ends_with_whitespace = /\s$/;
export const regex_ends_with_whitespaces = /[ \t\r\n]+$/;
/** Not \S because that also removes explicit whitespace defined through things like `&nbsp;` */
export const regex_not_whitespace = /[^ \t\r\n]/;
/** Not \s+ because that also includes explicit whitespace defined through things like `&nbsp;` */
export const regex_whitespaces_strict = /[ \t\n\r\f]+/g;

export const regex_only_whitespaces = /^[ \t\n\r\f]+$/;

export const regex_not_newline_characters = /[^\n]/g;

export const regex_is_valid_identifier = /^[a-zA-Z_$][a-zA-Z_$0-9]*$/;
// used in replace all to remove all invalid chars from a literal identifier
export const regex_invalid_identifier_chars = /(^[^a-zA-Z_$]|[^a-zA-Z0-9_$])/g;

export const regex_starts_with_vowel = /^[aeiou]/;
export const regex_heading_tags = /^h[1-6]$/;
export const regex_illegal_attribute_character = /(^[0-9-.])|[\^$@%&#?!|()[\]{}^*+~;]/;
export const regex_bidirectional_control_characters =
	/[\u202a\u202b\u202c\u202d\u202e\u2066\u2067\u2068\u2069]+/g;
export const regex_js_code = /^\W*javascript:/i;
export const regex_redundant_img_alt = /\b(image|picture|photo)\b/i;


Review comment (suggestion):
```suggestion
export const regex_js_prefix = /^\W*javascript:/i;
```
clarifies the purpose of the regex 


Code after:
export const regex_whitespace = /\s/;
export const regex_whitespaces = /\s+/;
export const regex_starts_with_newline = /^\r?\n/;
export const regex_starts_with_whitespace = /^\s/;
export const regex_starts_with_whitespaces = /^[ \t\r\n]+/;
export const regex_ends_with_whitespace = /\s$/;
export const regex_ends_with_whitespaces = /[ \t\r\n]+$/;
/** Not \S because that also removes explicit whitespace defined through things like `&nbsp;` */
export const regex_not_whitespace = /[^ \t\r\n]/;
/** Not \s+ because that also includes explicit whitespace defined through things like `&nbsp;` */
export const regex_whitespaces_strict = /[ \t\n\r\f]+/g;

export const regex_only_whitespaces = /^[ \t\n\r\f]+$/;

export const regex_not_newline_characters = /[^\n]/g;

export const regex_is_valid_identifier = /^[a-zA-Z_$][a-zA-Z_$0-9]*$/;
// used in replace all to remove all invalid chars from a literal identifier
export const regex_invalid_identifier_chars = /(^[^a-zA-Z_$]|[^a-zA-Z0-9_$])/g;

export const regex_starts_with_vowel = /^[aeiou]/;
export const regex_heading_tags = /^h[1-6]$/;
export const regex_illegal_attribute_character = /(^[0-9-.])|[\^$@%&#?!|()[\]{}^*+~;]/;
export const regex_bidirectional_control_characters =
	/[\u202a\u202b\u202c\u202d\u202e\u2066\u2067\u2068\u2069]+/g;
export const regex_js_prefix = /^\W*javascript:/i;
export const regex_redundant_img_alt = /\b(image|picture|photo)\b/i;
Repository: sveltejs/svelte
File: packages/svelte/src/compiler/legacy.js (JavaScript)

Code before:
	}
}

/**
 * Transform our nice modern AST into the monstrosity emitted by Svelte 4
 * @param {string} source
 * @param {AST.Root} ast
 * @returns {Legacy.LegacyRoot}
 */
export function convert(source, ast) {
	const root = /** @type {AST.SvelteNode | Legacy.LegacySvelteNode} */ (ast);

	return /** @type {Legacy.LegacyRoot} */ (
		walk(root, null, {
			_(node, { next }) {
				// @ts-ignore
				delete node.metadata;
				next();
			},
			// @ts-ignore
			Root(node, { visit }) {
				const { instance, module, options } = node;

				// Insert svelte:options back into the root nodes
				if (/** @type {any} */ (options)?.__raw__) {
					let idx = node.fragment.nodes.findIndex((node) => /** @type {any} */ (options).end <= node.start);
					if (idx === -1) {
						idx = node.fragment.nodes.length;
					}

					node.fragment.nodes.splice(idx, 0, /** @type {any} */ (options).__raw__);
				}

				/** @type {number | null} */
				let start = null;

				/** @type {number | null} */
				let end = null;

				if (node.fragment.nodes.length > 0) {
					const first = /** @type {AST.BaseNode} */ (node.fragment.nodes.at(0));
					const last = /** @type {AST.BaseNode} */ (node.fragment.nodes.at(-1));

					start = first.start;
					end = last.end;

					while (/\s/.test(source[start])) start += 1;
					while (/\s/.test(source[end - 1])) end -= 1;
				}

				if (instance) {

Review comment (suggestion):
same as before; `options` would be `unknown` in TS7 without this cast.

Code after:
	}
}

/**
 * Transform our nice modern AST into the monstrosity emitted by Svelte 4
 * @param {string} source
 * @param {AST.Root} ast
 * @returns {Legacy.LegacyRoot}
 */
export function convert(source, ast) {
	const root = /** @type {AST.SvelteNode | Legacy.LegacySvelteNode} */ (ast);

	return /** @type {Legacy.LegacyRoot} */ (
		walk(root, null, {
			_(node, { next }) {
				// @ts-ignore
				delete node.metadata;
				next();
			},
			// @ts-ignore
			Root(node, { visit }) {
				const { instance, module, options } = node;

				// Insert svelte:options back into the root nodes
				if (/** @type {any} */ (options)?.__raw__) {
					let idx = node.fragment.nodes.findIndex(
						(node) => /** @type {any} */ (options).end <= node.start
					);
					if (idx === -1) {
						idx = node.fragment.nodes.length;
					}

					node.fragment.nodes.splice(idx, 0, /** @type {any} */ (options).__raw__);
				}

				/** @type {number | null} */
				let start = null;

				/** @type {number | null} */
				let end = null;

				if (node.fragment.nodes.length > 0) {
					const first = /** @type {AST.BaseNode} */ (node.fragment.nodes.at(0));
					const last = /** @type {AST.BaseNode} */ (node.fragment.nodes.at(-1));

					start = first.start;
					end = last.end;

					while (/\s/.test(source[start])) start += 1;
					while (/\s/.test(source[end - 1])) end -= 1;
				}
Repository: sveltejs/svelte
File: packages/svelte/src/internal/client/reactivity/equality.js (JavaScript)

Code before:
/** @import { Equals } from '#client' */

/** @type {Equals} */
export const equals = function(value) {
	return value === this.v;
}

/**
 * @param {unknown} a
 * @param {unknown} b
 * @returns {boolean}
 */
export function safe_not_equal(a, b) {
	return a != a
		? b == b
		: a !== b || (a !== null && typeof a === 'object') || typeof a === 'function';
}

/**
 * @param {unknown} a
 * @param {unknown} b
 * @returns {boolean}
 */
export function not_equal(a, b) {
	return a !== b;
}

/** @type {Equals} */
export const safe_equals = function(value) {

Review comment (suggestion):
`@type` no longer works on function declarations because there's no equivalent in TS. But it does work with variables initialised to function expressions because of contextual typing.

Code after:
/** @import { Equals } from '#client' */

/** @type {Equals} */
export function equals(value) {
	return value === this.v;
}

/**
 * @param {unknown} a
 * @param {unknown} b
 * @returns {boolean}
 */
export function safe_not_equal(a, b) {
	return a != a
		? b == b
		: a !== b || (a !== null && typeof a === 'object') || typeof a === 'function';
}

/**
 * @param {unknown} a
 * @param {unknown} b
 * @returns {boolean}
 */
export function not_equal(a, b) {
	return a !== b;
}

/** @type {Equals} */
export function safe_equals(value) {
Repository: sveltejs/svelte
File: packages/svelte/src/internal/client/reactivity/equality.js (JavaScript)

Code before:
/** @import { Equals } from '#client' */

/** @type {Equals} */
export const equals = function (value) {
	return value === this.v;
};

/**
 * @param {unknown} a
 * @param {unknown} b
 * @returns {boolean}
 */
export function safe_not_equal(a, b) {
	return a != a
		? b == b
		: a !== b || (a !== null && typeof a === 'object') || typeof a === 'function';
}

/**
 * @param {unknown} a
 * @param {unknown} b
 * @returns {boolean}
 */
export function not_equal(a, b) {
	return a !== b;
}

/** @type {Equals} */
export const safe_equals = function (value) {

Review comment (suggestion):
sigh, this is actually one of the few places I liked JSDoc _more_ than TS -- the fact that there's no way to type `function` declarations in TS grinds my gears!

FWIW this actually is going to negatively impact us -- in our language tools, whenever you `export function load` from a `+page` file, we insert `/** @type {Load} */` above it to provide you generated types without you having to explicitly import and use them. I'm sure we can find a workaround but it'll at least be somewhat more difficult now, as we can't just change `function` declarations to `const {name} = () => {}` as it's semantically different

Code after:
/** @import { Equals } from '#client' */

/** @type {Equals} */
export function equals(value) {
	return value === this.v;
}

/**
 * @param {unknown} a
 * @param {unknown} b
 * @returns {boolean}
 */
export function safe_not_equal(a, b) {
	return a != a
		? b == b
		: a !== b || (a !== null && typeof a === 'object') || typeof a === 'function';
}

/**
 * @param {unknown} a
 * @param {unknown} b
 * @returns {boolean}
 */
export function not_equal(a, b) {
	return a !== b;
}

/** @type {Equals} */
export function safe_equals(value) {
Repository: sveltejs/svelte
File: packages/svelte/src/compiler/phases/3-transform/client/visitors/CallExpression.js (JavaScript)

Code before:
		case '$effect.root':
			return b.call(
				'$.effect_root',
				.../** @type {Expression[]} */ (node.arguments.map((arg) => context.visit(arg)))
			);

		case '$effect.pending':
			return b.call('$.pending');

		case '$inspect':
		case '$inspect().with':
			return transform_inspect_rune(node, context);
	}

	if (
		dev &&
		node.callee.type === 'MemberExpression' &&
		node.callee.object.type === 'Identifier' &&
		node.callee.object.name === 'console' &&
		context.state.scope.get('console') === null &&
		node.callee.property.type === 'Identifier' &&
		['debug', 'dir', 'error', 'group', 'groupCollapsed', 'info', 'log', 'trace', 'warn'].includes(
			node.callee.property.name
		) &&
		node.arguments.some(
			(arg) => arg.type === 'SpreadElement' || context.state.scope.evaluate(arg).values.has(UNKNOWN)
		)
	) {
		return b.call(
			node.callee,
			b.spread(
				b.call(
					'$.log_if_contains_state',
					b.literal(node.callee.property.name),
					.../** @type {Expression[]} */ (node.arguments.map((arg) => context.visit(arg)))
				)
			)
		);
	}

	context.next();
}


Review comment (suggestion):
Evaluation of SpreadElement will result in UNKNOWN anyway, so the first check can be removed, right?
And maybe it's cleaner to add `has`/`is_unknown` prop-flag to `Evaluation` and use it here.

Code after:
		case '$effect.root':
			return b.call(
				'$.effect_root',
				.../** @type {Expression[]} */ (node.arguments.map((arg) => context.visit(arg)))
			);

		case '$effect.pending':
			return b.call('$.pending');

		case '$inspect':
		case '$inspect().with':
			return transform_inspect_rune(node, context);
	}

	if (
		dev &&
		node.callee.type === 'MemberExpression' &&
		node.callee.object.type === 'Identifier' &&
		node.callee.object.name === 'console' &&
		context.state.scope.get('console') === null &&
		node.callee.property.type === 'Identifier' &&
		['debug', 'dir', 'error', 'group', 'groupCollapsed', 'info', 'log', 'trace', 'warn'].includes(
			node.callee.property.name
		) &&
		node.arguments.some(
			(arg) => arg.type === 'SpreadElement' || context.state.scope.evaluate(arg).has_unknown
		)
	) {
		return b.call(
			node.callee,
			b.spread(
				b.call(
					'$.log_if_contains_state',
					b.literal(node.callee.property.name),
					.../** @type {Expression[]} */ (node.arguments.map((arg) => context.visit(arg)))
				)
			)
		);
	}

	context.next();
}
Repository: sveltejs/svelte
File: packages/svelte/src/internal/client/dom/blocks/each.js (JavaScript)

Code before:

		if ((item.e.f & INERT) !== 0) {
			resume_effect(item.e);
			if (is_animated) {
				item.a?.unfix();
				(to_animate ??= new Set()).delete(item);
			}
		}

		if (item !== current) {
			if (seen !== undefined && seen.has(item)) {
				if (matched.length < stashed.length) {
					// more efficient to move later items to the front
					var start = stashed[0];
					var j;

					// full key uniqueness check is dev-only,
					// key duplicates cause crushing only due to `matched` being empty
					if (matched.length === 0) {
						var map = new Map();
						for (j = 0; j < length; j += 1) {
							var k = get_key(array[j], j);
							if (map.has(k)) {
								k = String(k);
								if (k.startsWith('[object ')) k = null;
								each_key_duplicate(String(j), String(map.get(k)), k);
							}
							map.set(k, j);
						}
					}

					prev = start.prev;

					var a = matched[0];
					var b = matched[matched.length - 1];

					for (j = 0; j < matched.length; j += 1) {
						move(matched[j], start, anchor);
					}

					for (j = 0; j < stashed.length; j += 1) {
						seen.delete(stashed[j]);
					}

					link(state, a.prev, b.next);
					link(state, prev, a);
					link(state, b, start);

					current = start;
					prev = b;
					i -= 1;

Review comment (refactor):
`k` is now a string so the map lookup returns undefined, so the test's error message says `Keyed each block has duplicate key 1 at indexes 1 and undefined`

Code after:
		if ((effect.f & INERT) !== 0) {
			resume_effect(effect);
			if (is_animated) {
				effect.nodes?.a?.unfix();
				(to_animate ??= new Set()).delete(effect);
			}
		}

		if (effect !== current) {
			if (seen !== undefined && seen.has(effect)) {
				if (matched.length < stashed.length) {
					// more efficient to move later items to the front
					var start = stashed[0];
					var j;

					prev = start.prev;

					var a = matched[0];
					var b = matched[matched.length - 1];

					for (j = 0; j < matched.length; j += 1) {
						move(matched[j], start, anchor);
					}

					for (j = 0; j < stashed.length; j += 1) {
						seen.delete(stashed[j]);
					}

					link(state, a.prev, b.next);
					link(state, prev, a);
					link(state, b, start);

					current = start;
					prev = b;
					i -= 1;

					matched = [];
					stashed = [];
				} else {
					// more efficient to move earlier items to the back
					seen.delete(effect);
					move(effect, current, anchor);

					link(state, effect.prev, effect.next);
					link(state, effect, prev === null ? state.effect.first : prev.next);
					link(state, prev, effect);

					prev = effect;
				}

				continue;