function extract_svelte_ignore(offset, text, runes) {
	const match = regex_svelte_ignore.exec(text);
	if (!match) return [];

	let length = match[0].length;
	offset += length;

	/** @type {string[]} */
	const ignores = [];

	if (runes) {
		// Warnings have to be separated by commas, everything after is interpreted as prose
		for (const match of text.slice(length).matchAll(/([\w$-]+)(,)?/gm)) {
			const code = match[1];

			if (codes.includes(code)) {
				ignores.push(code);
			} else {
				const replacement = replacements[code] ?? code.replace(/-/g, '_');

				// The type cast is for some reason necessary to pass the type check in CI
				const start = offset + /** @type {number} */ (match.index);
				const end = start + code.length;

				if (codes.includes(replacement)) {
					w.legacy_code({ start, end }, code, replacement);
				} else {
					const suggestion = fuzzymatch(code, codes);
					w.unknown_code({ start, end }, code, suggestion);
				}
			}

			if (!match[2]) {
				break;
			}
		}
	} else {
		// Non-runes mode: lax parsing, backwards compat with old codes
		for (const match of text.slice(length).matchAll(/[\w$-]+/gm)) {
			const code = match[0];

			ignores.push(code);

			if (!codes.includes(code)) {
				const replacement = replacements[code] ?? code.replace(/-/g, '_');

				if (codes.includes(replacement)) {
					ignores.push(replacement);
				}
			}
		}
	}

	return ignores;
}
@param {number} offset
@param {string} text
@param {boolean} runes
@returns {string[]}