From 3a92c8a9f06a772ce3530fdb6e06ebdf0aa0b49d Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 24 Mar 2026 04:33:40 +0000 Subject: [PATCH 1/5] =?UTF-8?q?fix:=20isAncestor=E5=88=A4=E5=AE=9A?= =?UTF-8?q?=E3=81=A7=E3=83=91=E3=82=B9=E5=A2=83=E7=95=8C=E3=82=92=E8=80=83?= =?UTF-8?q?=E6=85=AE=E3=81=99=E3=82=8B=E3=82=88=E3=81=86endsWith('/')?= =?UTF-8?q?=E3=83=81=E3=82=A7=E3=83=83=E3=82=AF=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 祖先になり得るのはディレクトリ(stemが'/'で終わるノード)のみであるため、 startsWith判定の前にurl.stem.endsWith('/')を追加。 これにより /foo/bar.html が /foo/bar_baz.html の祖先と誤判定される問題を修正。 Closes #866 https://claude.ai/code/session_01KTVELzSgVqvC97KB73S6RF --- .../shared/src/path-list-to-tree.spec.ts | 19 +++++++++++++++++++ .../@d-zero/shared/src/path-list-to-tree.ts | 5 ++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/packages/@d-zero/shared/src/path-list-to-tree.spec.ts b/packages/@d-zero/shared/src/path-list-to-tree.spec.ts index b0141f44..c54af448 100644 --- a/packages/@d-zero/shared/src/path-list-to-tree.spec.ts +++ b/packages/@d-zero/shared/src/path-list-to-tree.spec.ts @@ -385,6 +385,25 @@ test('Options: currentPath', () => { }); }); +test('Options: currentPath does not false-positive isAncestor on sibling prefix', () => { + const result = pathListToTree( + ['/', '/foo/index.html', '/foo/bar.html', '/foo/bar_baz.html'], + { + currentPath: '/foo/bar_baz.html', + }, + ); + + const foo = result.children[0]!; + expect(foo.stem).toBe('/foo/'); + expect(foo.isAncestor).toBe(true); + + const bar = foo.children.find((c) => c.stem === '/foo/bar')!; + expect(bar.isAncestor).toBe(false); + + const barBaz = foo.children.find((c) => c.stem === '/foo/bar_baz')!; + expect(barBaz.current).toBe(true); +}); + test('Options: addMetaData adds meta to every node', () => { const result = pathListToTree(['/', '/a/', '/a/b', '/a/c/'], { addMetaData: (node) => ({ diff --git a/packages/@d-zero/shared/src/path-list-to-tree.ts b/packages/@d-zero/shared/src/path-list-to-tree.ts index aac9cfa2..fad4ceff 100644 --- a/packages/@d-zero/shared/src/path-list-to-tree.ts +++ b/packages/@d-zero/shared/src/path-list-to-tree.ts @@ -118,7 +118,10 @@ export function pathListToTree>( stem: url.stem, depth: url.depth, current, - isAncestor: !current && currentPath ? currentPath.startsWith(url.stem) : false, + isAncestor: + !current && currentPath + ? url.stem.endsWith('/') && currentPath.startsWith(url.stem) + : false, children: [], }); } From 8a6c7b8e1081c2a3b5eb0fd64424339f18edc141 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 24 Mar 2026 04:34:35 +0000 Subject: [PATCH 2/5] =?UTF-8?q?chore:=20import=E9=A0=86=E5=BA=8F=E3=81=AE?= =?UTF-8?q?=E6=95=B4=E7=90=86=E3=81=A8=E4=B8=8D=E8=A6=81=E3=81=AA=E9=9D=9E?= =?UTF-8?q?null=E3=82=A2=E3=82=B5=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3?= =?UTF-8?q?=E3=81=AE=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://claude.ai/code/session_01KTVELzSgVqvC97KB73S6RF --- packages/@d-zero/a11y-check-core/src/scenario-child-process.ts | 3 +-- packages/@d-zero/a11y-check-core/src/scenario-main-process.ts | 3 +-- packages/@d-zero/a11y-check/src/cli.ts | 2 +- packages/@d-zero/archaeologist/src/cli.ts | 2 +- packages/@d-zero/print/src/cli.ts | 2 +- 5 files changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/@d-zero/a11y-check-core/src/scenario-child-process.ts b/packages/@d-zero/a11y-check-core/src/scenario-child-process.ts index 4aed1c27..81b9d3cc 100644 --- a/packages/@d-zero/a11y-check-core/src/scenario-child-process.ts +++ b/packages/@d-zero/a11y-check-core/src/scenario-child-process.ts @@ -1,5 +1,6 @@ import type { Result, NeedAnalysis, Passed, Scenario } from './types.js'; +import { importScenarios, type Violation } from '@d-zero/a11y-check-core'; import { createChildProcess } from '@d-zero/puppeteer-dealer'; import { beforePageScan, @@ -9,8 +10,6 @@ import { import { Cache } from '@d-zero/shared/cache'; import c from 'ansi-colors'; -import { importScenarios, type Violation } from '@d-zero/a11y-check-core'; - export type ChildProcessParams = { readonly scenarios: readonly Scenario[]; readonly cacheDir: string; diff --git a/packages/@d-zero/a11y-check-core/src/scenario-main-process.ts b/packages/@d-zero/a11y-check-core/src/scenario-main-process.ts index 46d3f218..a35577b2 100644 --- a/packages/@d-zero/a11y-check-core/src/scenario-main-process.ts +++ b/packages/@d-zero/a11y-check-core/src/scenario-main-process.ts @@ -11,13 +11,12 @@ import type { DealOptions } from '@d-zero/dealer'; import path from 'node:path'; +import { importScenarios, type Violation } from '@d-zero/a11y-check-core'; import { createProcess, deal } from '@d-zero/puppeteer-dealer'; import c from 'ansi-colors'; import { cleanResults } from './clean-results.js'; -import { importScenarios, type Violation } from '@d-zero/a11y-check-core'; - /** * * @param urlList diff --git a/packages/@d-zero/a11y-check/src/cli.ts b/packages/@d-zero/a11y-check/src/cli.ts index 9c1fc095..8557044f 100644 --- a/packages/@d-zero/a11y-check/src/cli.ts +++ b/packages/@d-zero/a11y-check/src/cli.ts @@ -79,7 +79,7 @@ let a11yOptions: A11yCheckOptions = { }; if (hasConfigFile) { - const { urlList, hooks } = await readConfig(options.listfile!); + const { urlList, hooks } = await readConfig(options.listfile); list.push(...urlList); a11yOptions = { ...a11yOptions, diff --git a/packages/@d-zero/archaeologist/src/cli.ts b/packages/@d-zero/archaeologist/src/cli.ts index 3c34a24a..ddc61715 100644 --- a/packages/@d-zero/archaeologist/src/cli.ts +++ b/packages/@d-zero/archaeologist/src/cli.ts @@ -76,7 +76,7 @@ const { options, hasConfigFile } = createCLI({ }); if (hasConfigFile) { - const { pairList, hooks } = await readConfig(options.listfile!); + const { pairList, hooks } = await readConfig(options.listfile); await analyze(pairList, { hooks, types: options.type ? parseList(options.type) : undefined, diff --git a/packages/@d-zero/print/src/cli.ts b/packages/@d-zero/print/src/cli.ts index 9391b9e0..3438497f 100644 --- a/packages/@d-zero/print/src/cli.ts +++ b/packages/@d-zero/print/src/cli.ts @@ -76,7 +76,7 @@ const deviceNames = options.devices ? parseList(options.devices) : undefined; const devices = parseDevicesOption(deviceNames); if (hasConfigFile) { - const { urlList, hooks } = await readConfig(options.listfile!); + const { urlList, hooks } = await readConfig(options.listfile); await print(urlList, { type, limit: options.limit, From 946a459036af0783eade8e5592e7eb12c53b772d Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 24 Mar 2026 04:39:58 +0000 Subject: [PATCH 3/5] =?UTF-8?q?test:=20isAncestor=E5=88=A4=E5=AE=9A?= =?UTF-8?q?=E3=81=AE=E5=9B=9E=E5=B8=B0=E3=83=86=E3=82=B9=E3=83=88=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 同名プレフィックスのディレクトリとファイルが共存するケース - currentPathがルートの場合 - 深いネストでの祖先チェーン検証 https://claude.ai/code/session_01KTVELzSgVqvC97KB73S6RF --- .../shared/src/path-list-to-tree.spec.ts | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/packages/@d-zero/shared/src/path-list-to-tree.spec.ts b/packages/@d-zero/shared/src/path-list-to-tree.spec.ts index c54af448..6dec98a6 100644 --- a/packages/@d-zero/shared/src/path-list-to-tree.spec.ts +++ b/packages/@d-zero/shared/src/path-list-to-tree.spec.ts @@ -404,6 +404,55 @@ test('Options: currentPath does not false-positive isAncestor on sibling prefix' expect(barBaz.current).toBe(true); }); +test('Options: currentPath file is not treated as child of same-prefix directory', () => { + const result = pathListToTree(['/', '/foo/', '/foo/bar/', '/foo/bar.html'], { + currentPath: '/foo/bar.html', + }); + + const foo = result.children[0]!; + expect(foo.stem).toBe('/foo/'); + expect(foo.isAncestor).toBe(true); + + const fooBarDir = foo.children.find((c) => c.stem === '/foo/bar/')!; + expect(fooBarDir.isAncestor).toBe(false); + + const fooBarFile = foo.children.find((c) => c.stem === '/foo/bar')!; + expect(fooBarFile.current).toBe(true); +}); + +test('Options: currentPath as root marks root current and no ancestors', () => { + const result = pathListToTree(['/', '/a/', '/b.html'], { + currentPath: '/', + }); + + expect(result.current).toBe(true); + expect(result.isAncestor).toBe(false); + expect(result.children[0]!.isAncestor).toBe(false); + expect(result.children[1]!.isAncestor).toBe(false); +}); + +test('Options: currentPath deep nesting marks all ancestor directories', () => { + const result = pathListToTree(['/', '/a/', '/a/b/', '/a/b/c.html'], { + currentPath: '/a/b/c.html', + }); + + expect(result.isAncestor).toBe(true); + expect(result.current).toBe(false); + + const a = result.children[0]!; + expect(a.stem).toBe('/a/'); + expect(a.isAncestor).toBe(true); + + const ab = a.children[0]!; + expect(ab.stem).toBe('/a/b/'); + expect(ab.isAncestor).toBe(true); + + const abc = ab.children[0]!; + expect(abc.stem).toBe('/a/b/c'); + expect(abc.current).toBe(true); + expect(abc.isAncestor).toBe(false); +}); + test('Options: addMetaData adds meta to every node', () => { const result = pathListToTree(['/', '/a/', '/a/b', '/a/c/'], { addMetaData: (node) => ({ From e5c668991001ccffcf264c60bea0a677da3cdfe4 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 24 Mar 2026 04:53:05 +0000 Subject: [PATCH 4/5] =?UTF-8?q?fix:=20readConfig=E3=81=AE=E5=BC=95?= =?UTF-8?q?=E6=95=B0=E3=81=AB=E9=9D=9Enull=E3=82=A2=E3=82=B5=E3=83=BC?= =?UTF-8?q?=E3=82=B7=E3=83=A7=E3=83=B3=E3=82=92=E5=BE=A9=E5=85=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hasConfigFileガード内ではlistfileは必ず存在するため 非nullアサーション(!)が正しい。削除するとTS2345エラーになる。 https://claude.ai/code/session_01KTVELzSgVqvC97KB73S6RF --- packages/@d-zero/a11y-check/src/cli.ts | 2 +- packages/@d-zero/archaeologist/src/cli.ts | 2 +- packages/@d-zero/print/src/cli.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/@d-zero/a11y-check/src/cli.ts b/packages/@d-zero/a11y-check/src/cli.ts index 8557044f..9c1fc095 100644 --- a/packages/@d-zero/a11y-check/src/cli.ts +++ b/packages/@d-zero/a11y-check/src/cli.ts @@ -79,7 +79,7 @@ let a11yOptions: A11yCheckOptions = { }; if (hasConfigFile) { - const { urlList, hooks } = await readConfig(options.listfile); + const { urlList, hooks } = await readConfig(options.listfile!); list.push(...urlList); a11yOptions = { ...a11yOptions, diff --git a/packages/@d-zero/archaeologist/src/cli.ts b/packages/@d-zero/archaeologist/src/cli.ts index ddc61715..3c34a24a 100644 --- a/packages/@d-zero/archaeologist/src/cli.ts +++ b/packages/@d-zero/archaeologist/src/cli.ts @@ -76,7 +76,7 @@ const { options, hasConfigFile } = createCLI({ }); if (hasConfigFile) { - const { pairList, hooks } = await readConfig(options.listfile); + const { pairList, hooks } = await readConfig(options.listfile!); await analyze(pairList, { hooks, types: options.type ? parseList(options.type) : undefined, diff --git a/packages/@d-zero/print/src/cli.ts b/packages/@d-zero/print/src/cli.ts index 3438497f..9391b9e0 100644 --- a/packages/@d-zero/print/src/cli.ts +++ b/packages/@d-zero/print/src/cli.ts @@ -76,7 +76,7 @@ const deviceNames = options.devices ? parseList(options.devices) : undefined; const devices = parseDevicesOption(deviceNames); if (hasConfigFile) { - const { urlList, hooks } = await readConfig(options.listfile); + const { urlList, hooks } = await readConfig(options.listfile!); await print(urlList, { type, limit: options.limit, From aaeef4a4ff72a18e816420ee49a0ec0bb288df3f Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 24 Mar 2026 04:57:35 +0000 Subject: [PATCH 5/5] =?UTF-8?q?docs:=20isAncestor=E3=81=AEJSDoc=E3=82=92?= =?UTF-8?q?=E3=83=87=E3=82=A3=E3=83=AC=E3=82=AF=E3=83=88=E3=83=AA=E9=99=90?= =?UTF-8?q?=E5=AE=9A=E3=81=AE=E5=8B=95=E4=BD=9C=E3=81=AB=E5=90=88=E3=82=8F?= =?UTF-8?q?=E3=81=9B=E3=81=A6=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://claude.ai/code/session_01KTVELzSgVqvC97KB73S6RF --- packages/@d-zero/shared/src/path-list-to-tree.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@d-zero/shared/src/path-list-to-tree.ts b/packages/@d-zero/shared/src/path-list-to-tree.ts index fad4ceff..affb48f6 100644 --- a/packages/@d-zero/shared/src/path-list-to-tree.ts +++ b/packages/@d-zero/shared/src/path-list-to-tree.ts @@ -51,7 +51,7 @@ export type Node> = { depth: number; /** True if this node is the current path. */ current: boolean; - /** True if the current path is under this node. */ + /** True if this node is a directory ancestor of the current path. Only directory nodes (stem ending with '/') can be ancestors. */ isAncestor: boolean; /** Present when the node was created as a virtual parent (no real file). */ virtual?: true;