From 9763c3f279ef080063f6eba26d7c83034279a418 Mon Sep 17 00:00:00 2001 From: William Thorsen Date: Mon, 10 Aug 2026 04:38:27 -0700 Subject: [PATCH 1/3] agents|refactor: Hoist unreadable for-of expressions in library code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Loop headers under `packages/agents/src` now bind their iterable expression to a named `const` above the loop, clearing `unicorn/no-unreadable-for-of-expression` from the package's library code. `for…of` resolves its right-hand side once before iterating, so every hoist preserves behavior. --- packages/agents/src/commands/install.ts | 3 ++- packages/agents/src/commands/library-list.ts | 12 ++++++++---- packages/agents/src/lib/content-validation.ts | 6 ++++-- packages/agents/src/lib/dependency-resolver.ts | 6 ++++-- packages/agents/src/lib/library-catalog.ts | 3 ++- packages/agents/src/lib/rendered-tree.ts | 3 ++- packages/agents/src/lib/skill-transform.ts | 3 ++- packages/agents/src/lib/support-deploy.ts | 6 ++++-- 8 files changed, 28 insertions(+), 14 deletions(-) diff --git a/packages/agents/src/commands/install.ts b/packages/agents/src/commands/install.ts index 20da8a1e..c435038a 100644 --- a/packages/agents/src/commands/install.ts +++ b/packages/agents/src/commands/install.ts @@ -234,7 +234,8 @@ async function installSupportDirectories( // Install non-skill support directories (e.g. `_data`, which skills reference at runtime by absolute path). What // counts as one is `listSupportEntries`, shared with `validate` so the pass that checks these and the pass that // deploys them cannot come to disagree about which entries they are. - for (const entry of await listSupportEntries(skillsSrcDir)) { + const supportEntries = await listSupportEntries(skillsSrcDir); + for (const entry of supportEntries) { const result = await installSkillEntry( path.join(skillsSrcDir, entry), path.join(skillsDestDir, entry), diff --git a/packages/agents/src/commands/library-list.ts b/packages/agents/src/commands/library-list.ts index 70bde39c..31dc3040 100644 --- a/packages/agents/src/commands/library-list.ts +++ b/packages/agents/src/commands/library-list.ts @@ -153,7 +153,8 @@ function compareRows(a: LibraryRow, b: LibraryRow): number { async function listCollections(contentDir: string): Promise> { const dir = path.join(contentDir, ARTIFACT_TYPES.collection.contentPath); const entries: Array = []; - for (const file of await listVisibleMarkdownFiles(dir)) { + const files = await listVisibleMarkdownFiles(dir); + for (const file of files) { const content = await readFile(path.join(dir, file), 'utf8'); const entry = buildEntryOrSkip('collection', file, () => { const meta = readNameAndDescription(content); @@ -174,7 +175,8 @@ async function listCollections(contentDir: string): Promise async function listRulebooks(contentDir: string): Promise> { const dir = path.join(contentDir, ARTIFACT_TYPES.rulebook.contentPath); const entries: Array = []; - for (const file of await listVisibleMarkdownFiles(dir)) { + const files = await listVisibleMarkdownFiles(dir); + for (const file of files) { const content = await readFile(path.join(dir, file), 'utf8'); const entry = buildEntryOrSkip('rulebook', file, () => { const { rulebook } = parseRulebookFile(content, file); @@ -195,7 +197,8 @@ async function listRulebooks(contentDir: string): Promise> async function listSkills(contentDir: string): Promise> { const dir = path.join(contentDir, ARTIFACT_TYPES.skill.contentPath); const entries: Array = []; - for (const name of await listSkillDirectories(dir)) { + const names = await listSkillDirectories(dir); + for (const name of names) { const content = await readFile(path.join(dir, name, 'SKILL.md'), 'utf8'); const entry = buildEntryOrSkip('skill', name, () => { const meta = readNameAndDescription(content); @@ -216,7 +219,8 @@ async function listSkills(contentDir: string): Promise> { async function listSubagents(contentDir: string): Promise> { const dir = path.join(contentDir, ARTIFACT_TYPES.subagent.contentPath); const entries: Array = []; - for (const file of await listVisibleMarkdownFiles(dir)) { + const files = await listVisibleMarkdownFiles(dir); + for (const file of files) { const content = await readFile(path.join(dir, file), 'utf8'); const entry = buildEntryOrSkip('subagent', file, () => { const meta = readNameAndDescription(content); diff --git a/packages/agents/src/lib/content-validation.ts b/packages/agents/src/lib/content-validation.ts index 2e051e29..4d8f6572 100644 --- a/packages/agents/src/lib/content-validation.ts +++ b/packages/agents/src/lib/content-validation.ts @@ -349,7 +349,8 @@ async function renderSupportEntries( const skillsDir = path.join(root, ARTIFACT_TYPES.skill.contentPath); const raised: Array = []; - for (const name of await listSupportEntries(skillsDir)) { + const supportEntries = await listSupportEntries(skillsDir); + for (const name of supportEntries) { const relPath = `${ARTIFACT_TYPES.skill.contentPath}/${name}`; try { await renderSupportEntry(path.join(skillsDir, name), name, root, skillContext); @@ -420,7 +421,8 @@ async function resolveSeedClosures( const reached = { rulebook: new Set(), skill: new Set(), subagent: new Set() }; for (const type of ARTIFACT_TYPE_VALUES) { - for (const slug of seeds[type] ?? []) { + const slugs = seeds[type] ?? []; + for (const slug of slugs) { const seed: DirectArtifacts = { [type]: [slug] }; try { const closure = await resolveClosure(seed, resolver); diff --git a/packages/agents/src/lib/dependency-resolver.ts b/packages/agents/src/lib/dependency-resolver.ts index 959e8d21..ff74a626 100644 --- a/packages/agents/src/lib/dependency-resolver.ts +++ b/packages/agents/src/lib/dependency-resolver.ts @@ -56,7 +56,8 @@ export async function resolveClosure(direct: DirectArtifacts, resolver: SourceRe onPath.add(id); const edges = await readArtifactEdges(type, slug, resolver); for (const edgeType of ARTIFACT_TYPE_VALUES) { - for (const edgeSlug of edges[edgeType] ?? []) { + const edgeSlugs = edges[edgeType] ?? []; + for (const edgeSlug of edgeSlugs) { await visit(edgeType, edgeSlug, [...trail, id]); } } @@ -64,7 +65,8 @@ export async function resolveClosure(direct: DirectArtifacts, resolver: SourceRe } for (const type of ARTIFACT_TYPE_VALUES) { - for (const slug of direct[type] ?? []) { + const slugs = direct[type] ?? []; + for (const slug of slugs) { await visit(type, slug, []); } } diff --git a/packages/agents/src/lib/library-catalog.ts b/packages/agents/src/lib/library-catalog.ts index fa52e2f0..b6248b86 100644 --- a/packages/agents/src/lib/library-catalog.ts +++ b/packages/agents/src/lib/library-catalog.ts @@ -60,7 +60,8 @@ export async function isSkillDirectory(entryDir: string): Promise { */ export async function listSkillDirectories(skillsDir: string): Promise> { const names: Array = []; - for (const name of await listVisibleSubdirectories(skillsDir)) { + const subdirectoryNames = await listVisibleSubdirectories(skillsDir); + for (const name of subdirectoryNames) { if (await isSkillDirectory(path.join(skillsDir, name))) { names.push(name); } diff --git a/packages/agents/src/lib/rendered-tree.ts b/packages/agents/src/lib/rendered-tree.ts index 1a7b3e28..ed0c9d09 100644 --- a/packages/agents/src/lib/rendered-tree.ts +++ b/packages/agents/src/lib/rendered-tree.ts @@ -44,7 +44,8 @@ async function copyFileIfChanged(srcPath: string, destPath: string): Promise): Promise { - for (const entry of await readdir(path.join(destDir, relDir), { withFileTypes: true })) { + const entries = await readdir(path.join(destDir, relDir), { withFileTypes: true }); + for (const entry of entries) { const rel = relDir === '' ? entry.name : `${relDir}/${entry.name}`; const absPath = path.join(destDir, rel); if (entry.isDirectory()) { diff --git a/packages/agents/src/lib/skill-transform.ts b/packages/agents/src/lib/skill-transform.ts index a3c6350e..b84514bb 100644 --- a/packages/agents/src/lib/skill-transform.ts +++ b/packages/agents/src/lib/skill-transform.ts @@ -119,7 +119,8 @@ async function collectEntries( context: SkillDeployContext, out: Array, ): Promise { - for (const entry of await readdir(dir, { withFileTypes: true })) { + const entries = await readdir(dir, { withFileTypes: true }); + for (const entry of entries) { if (entry.name === '_partials' || isTestDirectory(entry.name) || entry.name.startsWith('.')) { continue; } diff --git a/packages/agents/src/lib/support-deploy.ts b/packages/agents/src/lib/support-deploy.ts index 3dd5d529..7e960bd5 100644 --- a/packages/agents/src/lib/support-deploy.ts +++ b/packages/agents/src/lib/support-deploy.ts @@ -24,7 +24,8 @@ export async function renderSourceSupport( const skillsDir = path.join(sourceDir, ARTIFACT_TYPES.skill.contentPath); const rendered: Array = []; - for (const name of await listSupportEntries(skillsDir)) { + const supportEntries = await listSupportEntries(skillsDir); + for (const name of supportEntries) { const srcPath = path.join(skillsDir, name); const entry = await renderSupportEntry(srcPath, name, sourceDir, context); switch (entry.kind) { @@ -68,7 +69,8 @@ export async function retractUndeclaredSourceSupport( sourcesRoot: string, outcome: SourceSupportOutcome, ): Promise { - for (const target of await listUndeclaredSourceSupport(sourcesRoot, outcome)) { + const targets = await listUndeclaredSourceSupport(sourcesRoot, outcome); + for (const target of targets) { await rm(target, { recursive: true, force: true }); } } From b741794027d355eb2f8814aded22ad499f3bc97d Mon Sep 17 00:00:00 2001 From: William Thorsen Date: Mon, 10 Aug 2026 04:40:32 -0700 Subject: [PATCH 2/3] agents|tests: Hoist unreadable for-of expressions in tests Loop headers across the agents test suite now bind their iterable expression to a named `const` above the loop, clearing the last `unicorn/no-unreadable-for-of-expression` sites in the package. No test's expectations or fixtures change. --- .../content/__tests__/collection-dispositions.unit.test.ts | 3 ++- .../content/__tests__/comment-discipline-reach.unit.test.ts | 3 ++- .../content/__tests__/content-link-resolution.unit.test.ts | 3 ++- .../agents/content/__tests__/content-rendering.unit.test.ts | 3 ++- .../__tests__/injection-point-placement.unit.test.ts | 6 ++++-- .../content/__tests__/prose-line-breaks-reach.unit.test.ts | 3 ++- .../__tests__/shared-guidance-references.unit.test.ts | 3 ++- .../agents/content/__tests__/spec-inlining.unit.test.ts | 6 ++++-- .../content/__tests__/vetted-store-conventions.unit.test.ts | 6 ++++-- .../src/commands/__tests__/install-prune.unit.test.ts | 6 ++++-- .../commands/__tests__/install-real-library.unit.test.ts | 3 ++- .../src/lib/__tests__/library-invocation-edges.unit.test.ts | 6 ++++-- 12 files changed, 34 insertions(+), 17 deletions(-) diff --git a/packages/agents/content/__tests__/collection-dispositions.unit.test.ts b/packages/agents/content/__tests__/collection-dispositions.unit.test.ts index 3d80793b..60b7911f 100644 --- a/packages/agents/content/__tests__/collection-dispositions.unit.test.ts +++ b/packages/agents/content/__tests__/collection-dispositions.unit.test.ts @@ -340,7 +340,8 @@ function listClosureIds(closure: ResolvedClosure): Array { async function readExplicitCollections(contentDir: string): Promise> { const collectionsDir = path.join(contentDir, ARTIFACT_TYPES.collection.contentPath); const found = new Map(); - for (const file of (await listVisibleMarkdownFiles(collectionsDir)).toSorted()) { + const files = (await listVisibleMarkdownFiles(collectionsDir)).toSorted(); + for (const file of files) { const slug = path.basename(file, '.md'); const members = readMembers(await readFile(path.join(collectionsDir, file), 'utf8'), `collection ${slug}`); if (members.kind === 'explicit') { diff --git a/packages/agents/content/__tests__/comment-discipline-reach.unit.test.ts b/packages/agents/content/__tests__/comment-discipline-reach.unit.test.ts index 44d7d9b1..450ab194 100644 --- a/packages/agents/content/__tests__/comment-discipline-reach.unit.test.ts +++ b/packages/agents/content/__tests__/comment-discipline-reach.unit.test.ts @@ -78,7 +78,8 @@ describe('comment-discipline reach', () => { it('no content file reaches the doctrine by reference', async () => { const violations: Array = []; - for (const file of await listMarkdownFiles(CONTENT_ROOT)) { + const files = await listMarkdownFiles(CONTENT_ROOT); + for (const file of files) { const content = await readFile(file, 'utf8'); for (const reference of RETIRED_REFERENCES) { if (content.includes(reference)) { diff --git a/packages/agents/content/__tests__/content-link-resolution.unit.test.ts b/packages/agents/content/__tests__/content-link-resolution.unit.test.ts index 794d469e..9953e869 100644 --- a/packages/agents/content/__tests__/content-link-resolution.unit.test.ts +++ b/packages/agents/content/__tests__/content-link-resolution.unit.test.ts @@ -139,7 +139,8 @@ async function findRulebookRejections(): Promise> { /** Recursively collects installable host `.md` files, skipping `_partials/` at any depth and dotfiles. */ async function collectHostFiles(dir: string, out: Array): Promise { - for (const entry of await readdir(dir, { withFileTypes: true })) { + const entries = await readdir(dir, { withFileTypes: true }); + for (const entry of entries) { if (entry.name === '_partials' || entry.name.startsWith('.') || isTestDirectory(entry.name)) { continue; } diff --git a/packages/agents/content/__tests__/content-rendering.unit.test.ts b/packages/agents/content/__tests__/content-rendering.unit.test.ts index b07380cc..6599cba2 100644 --- a/packages/agents/content/__tests__/content-rendering.unit.test.ts +++ b/packages/agents/content/__tests__/content-rendering.unit.test.ts @@ -16,7 +16,8 @@ const NBSP = '\u{A0}'; describe('content rendering', () => { it('no Markdown under content/ carries a non-breaking space', async () => { const offenders: Array = []; - for (const relativePath of await listContentMarkdown()) { + const relativePaths = await listContentMarkdown(); + for (const relativePath of relativePaths) { const body = await readFile(path.join(CONTENT_ROOT, relativePath), 'utf8'); if (body.includes(NBSP)) { offenders.push(relativePath); diff --git a/packages/agents/content/__tests__/injection-point-placement.unit.test.ts b/packages/agents/content/__tests__/injection-point-placement.unit.test.ts index 332ec430..6e77281d 100644 --- a/packages/agents/content/__tests__/injection-point-placement.unit.test.ts +++ b/packages/agents/content/__tests__/injection-point-placement.unit.test.ts @@ -55,7 +55,8 @@ const levelByPartial = new Map(); describe('injection-point placement', () => { it('no directive reparents the section following it', async () => { const violations: Array = []; - for (const relativePath of await listContentMarkdown()) { + const relativePaths = await listContentMarkdown(); + for (const relativePath of relativePaths) { violations.push(...(await findViolations(relativePath))); } @@ -78,7 +79,8 @@ async function findViolations(relativePath: string): Promise = []; const open: Array = []; - for (const token of await readStructure(relativePath)) { + const tokens = await readStructure(relativePath); + for (const token of tokens) { if (token.kind === 'injection') { open.push(token); continue; diff --git a/packages/agents/content/__tests__/prose-line-breaks-reach.unit.test.ts b/packages/agents/content/__tests__/prose-line-breaks-reach.unit.test.ts index d58ea62e..c357f00c 100644 --- a/packages/agents/content/__tests__/prose-line-breaks-reach.unit.test.ts +++ b/packages/agents/content/__tests__/prose-line-breaks-reach.unit.test.ts @@ -59,7 +59,8 @@ describe('prose-line-breaks reach', () => { it('is stated in no content file but the partial', async () => { const violations: Array = []; - for (const file of await listMarkdownFiles(CONTENT_ROOT)) { + const files = await listMarkdownFiles(CONTENT_ROOT); + for (const file of files) { const relativePath = path.relative(CONTENT_ROOT, file); if (relativePath === PARTIAL) continue; diff --git a/packages/agents/content/__tests__/shared-guidance-references.unit.test.ts b/packages/agents/content/__tests__/shared-guidance-references.unit.test.ts index 074d23c9..72e70eca 100644 --- a/packages/agents/content/__tests__/shared-guidance-references.unit.test.ts +++ b/packages/agents/content/__tests__/shared-guidance-references.unit.test.ts @@ -31,7 +31,8 @@ describe('shared guidance references', () => { const deployed = new Set(await listDeployedSkillNames()); const violations: Array = []; - for (const file of await listMarkdownFiles(SHARED_GUIDANCE_ROOT)) { + const files = await listMarkdownFiles(SHARED_GUIDANCE_ROOT); + for (const file of files) { const content = await readFile(file, 'utf8'); for (const slug of collectSkillReferences(content)) { if (!deployed.has(slug)) { diff --git a/packages/agents/content/__tests__/spec-inlining.unit.test.ts b/packages/agents/content/__tests__/spec-inlining.unit.test.ts index 669917f8..a6e6f49d 100644 --- a/packages/agents/content/__tests__/spec-inlining.unit.test.ts +++ b/packages/agents/content/__tests__/spec-inlining.unit.test.ts @@ -127,7 +127,8 @@ describe('output-shaping spec inlining', () => { it('inlines each spec exactly once', async () => { const expanded = await expandSkill(slug); - for (const heading of new Set(specs.map((spec) => spec.heading))) { + const headings = new Set(specs.map((spec) => spec.heading)); + for (const heading of headings) { expect(countOccurrences(expanded, heading), `${slug} repeats "${heading}"`).toBe(1); } }); @@ -135,7 +136,8 @@ describe('output-shaping spec inlining', () => { it('no skill still links to a relocated spec', async () => { const violations: Array = []; - for (const entry of await readdir(SKILLS_ROOT, { withFileTypes: true })) { + const entries = await readdir(SKILLS_ROOT, { withFileTypes: true }); + for (const entry of entries) { // `_`-prefixed entries are support directories; a directory with no `SKILL.md` (e.g. a bundled helper) is not // a skill either. Neither can carry a spec link. if (!entry.isDirectory() || entry.name.startsWith('_')) { diff --git a/packages/agents/content/__tests__/vetted-store-conventions.unit.test.ts b/packages/agents/content/__tests__/vetted-store-conventions.unit.test.ts index d0f8783e..03930ae2 100644 --- a/packages/agents/content/__tests__/vetted-store-conventions.unit.test.ts +++ b/packages/agents/content/__tests__/vetted-store-conventions.unit.test.ts @@ -119,7 +119,8 @@ function findConcreteStores(line: string, isFenced: boolean): Array { /** Reports every argument-position store name across the Markdown of the vetted collection's closure. */ async function findViolations(contentDir: string): Promise> { const violations: Array = []; - for (const relativePath of await listClosureFiles(contentDir)) { + const relativePaths = await listClosureFiles(contentDir); + for (const relativePath of relativePaths) { const lines = (await readFile(path.join(contentDir, relativePath), 'utf8')).split('\n'); let isFenced = false; for (const [index, line] of lines.entries()) { @@ -179,7 +180,8 @@ function listCodeSpans(text: string): Array { /** Lists every Markdown file under `relativeDir`, recursively, as paths relative to `root`. */ async function listMarkdownFilesUnder(root: string, relativeDir: string): Promise> { const foundFiles: Array = []; - for (const entry of await readDirEntries(path.join(root, relativeDir))) { + const entries = await readDirEntries(path.join(root, relativeDir)); + for (const entry of entries) { const relativePath = path.join(relativeDir, entry.name); if (entry.isDirectory()) { foundFiles.push(...(await listMarkdownFilesUnder(root, relativePath))); diff --git a/packages/agents/src/commands/__tests__/install-prune.unit.test.ts b/packages/agents/src/commands/__tests__/install-prune.unit.test.ts index 60bfc9d1..5044e026 100644 --- a/packages/agents/src/commands/__tests__/install-prune.unit.test.ts +++ b/packages/agents/src/commands/__tests__/install-prune.unit.test.ts @@ -159,10 +159,12 @@ describe('install stale-file pruning', () => { for (const [name, body] of Object.entries(shared)) { await writeFile(path.join(contentDir, 'guidance', 'shared', name), body, 'utf8'); } - for (const [name, body] of Object.entries(options.scripts ?? {})) { + const scripts = options.scripts ?? {}; + for (const [name, body] of Object.entries(scripts)) { await writeFile(path.join(contentDir, 'scripts', name), body, 'utf8'); } - for (const [dirName, files] of Object.entries(options.supportDirs ?? {})) { + const supportDirs = options.supportDirs ?? {}; + for (const [dirName, files] of Object.entries(supportDirs)) { const supportDir = path.join(contentDir, 'skills', dirName); await mkdir(supportDir, { recursive: true }); for (const [fileName, body] of Object.entries(files)) { diff --git a/packages/agents/src/commands/__tests__/install-real-library.unit.test.ts b/packages/agents/src/commands/__tests__/install-real-library.unit.test.ts index fb3f6c5f..261f41e9 100644 --- a/packages/agents/src/commands/__tests__/install-real-library.unit.test.ts +++ b/packages/agents/src/commands/__tests__/install-real-library.unit.test.ts @@ -162,7 +162,8 @@ async function collectBareRelativeLinks(baseDir: string): Promise Promise): Promise { - for (const entry of await readdir(dir)) { + const entries = await readdir(dir); + for (const entry of entries) { const full = path.join(dir, entry); const info = await lstat(full); if (info.isSymbolicLink()) { diff --git a/packages/agents/src/lib/__tests__/library-invocation-edges.unit.test.ts b/packages/agents/src/lib/__tests__/library-invocation-edges.unit.test.ts index 0897fa13..7371836a 100644 --- a/packages/agents/src/lib/__tests__/library-invocation-edges.unit.test.ts +++ b/packages/agents/src/lib/__tests__/library-invocation-edges.unit.test.ts @@ -79,7 +79,8 @@ describe('library invocation edges', () => { const literalRefRe = /(? = []; - for (const file of await listMarkdownFilesRecursively(contentDir)) { + const files = await listMarkdownFilesRecursively(contentDir); + for (const file of files) { const body = (await readFile(file, 'utf8')).replace(tokenRe, ''); for (const [, ref] of body.matchAll(literalRefRe)) { if (ref !== undefined && known.has(ref)) { @@ -95,7 +96,8 @@ describe('library invocation edges', () => { /** Lists every markdown file under `dir` recursively — the full set of deployed content the completeness guard scans. */ async function listMarkdownFilesRecursively(dir: string): Promise> { const found: Array = []; - for (const entry of await readdir(dir, { withFileTypes: true })) { + const entries = await readdir(dir, { withFileTypes: true }); + for (const entry of entries) { const full = path.join(dir, entry.name); if (entry.isDirectory()) { found.push(...(await listMarkdownFilesRecursively(full))); From 2b5e56a98409f9d2111f8d8b578b00d76611ee9b Mon Sep 17 00:00:00 2001 From: William Thorsen Date: Mon, 10 Aug 2026 04:42:03 -0700 Subject: [PATCH 3/3] agents|refactor: Promote no-unreadable-for-of-expression to an error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `unicorn/no-unreadable-for-of-expression` no longer sits on the agents deferral list, so a complex `for…of` header now fails the lint gate instead of accruing as an unread warning. --- packages/agents/.config/eslint/deferred-lint-rules.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/agents/.config/eslint/deferred-lint-rules.ts b/packages/agents/.config/eslint/deferred-lint-rules.ts index fd8da843..d914d673 100644 --- a/packages/agents/.config/eslint/deferred-lint-rules.ts +++ b/packages/agents/.config/eslint/deferred-lint-rules.ts @@ -3,7 +3,6 @@ export const deferredLintRules = { 'unicorn/no-duplicate-loops': 'warn', 'unicorn/no-incorrect-template-string-interpolation': 'warn', 'unicorn/no-top-level-assignment-in-function': 'warn', - 'unicorn/no-unreadable-for-of-expression': 'warn', 'unicorn/no-unsafe-string-replacement': 'warn', 'unicorn/prefer-await': 'warn', 'unicorn/prefer-iterator-to-array': 'warn',