-
Notifications
You must be signed in to change notification settings - Fork 209
Replace native turbowalk with pure TypeScript workspace package #22910
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
halgari
wants to merge
16
commits into
master
Choose a base branch
from
halgari/APP-405
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
5cc5c57
Replace native turbowalk with pure TypeScript workspace package
halgari 46ff372
Fix Bluebird return type and add concurrent lstat for performance
halgari 20776e3
Fix pre-existing Bluebird<void[]> vs Bluebird<void> type mismatch
halgari 2b01623
Fix strict TypeScript errors in turbowalk implementation
halgari a18c596
Add koffi FFI walk using FindFirstFileW/FindNextFileW on Windows
halgari b3640f0
Switch from FindFirstFileW to NtQueryDirectoryFile for bulk iteration
halgari c976709
Use 1KB buffer for NtQueryDirectoryFile (matches C++ addon)
halgari 71b3287
Add tests, proper error handling, and details option support
halgari a1f3df6
Document zero-copy optimization path blocked by Electron
halgari 081e04f
Clarify Electron V8 sandbox is compile-time, no runtime workaround
halgari e8eb55f
Merge master into halgari/APP-405
halgari d355ada
Merge origin/master into halgari/APP-405 and add turbowalk build script
halgari cf044df
Use .then(() => {}) convention in purgeLinks return type coercion
halgari 99ed6a0
Update pnpm-lock.yaml for turbowalk tsdown dependency
halgari ac6d4c1
Fix turbowalk build: rename tsdown config to .mts and format source f…
halgari e2c4cc5
Replace deprecated external with deps.neverBundle in turbowalk tsdown…
halgari File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| { | ||
| "name": "turbowalk", | ||
| "version": "4.0.0", | ||
| "description": "Fast directory walking using koffi FFI on Windows, fs on Linux", | ||
| "main": "src/index.ts", | ||
| "types": "src/index.ts", | ||
| "private": true, | ||
| "type": "commonjs", | ||
| "scripts": { | ||
| "build": "pnpm tsdown" | ||
| }, | ||
| "devDependencies": { | ||
| "bluebird": "catalog:", | ||
| "@types/bluebird": "catalog:", | ||
| "koffi": "^2.9.0", | ||
| "tsdown": "catalog:" | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,146 @@ | ||
| import * as fs from "fs"; | ||
| import * as os from "os"; | ||
| import * as path from "path"; | ||
|
|
||
| import { describe, it, expect, beforeAll, afterAll } from "vitest"; | ||
|
|
||
| import turbowalk, { type IEntry } from "./index"; | ||
|
|
||
| let tmpDir: string; | ||
|
|
||
| beforeAll(() => { | ||
| tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "turbowalk-test-")); | ||
|
|
||
| // Create a directory tree: | ||
| // root/ | ||
| // file1.txt (10 bytes) | ||
| // file2.dat (20 bytes) | ||
| // .hidden (0 bytes) | ||
| // subdir/ | ||
| // nested.txt (5 bytes) | ||
| // emptydir/ | ||
| // .hiddendir/ | ||
| // inside.txt (1 byte) | ||
| fs.writeFileSync(path.join(tmpDir, "file1.txt"), "0123456789"); | ||
| fs.writeFileSync(path.join(tmpDir, "file2.dat"), "01234567890123456789"); | ||
| fs.writeFileSync(path.join(tmpDir, ".hidden"), ""); | ||
| fs.mkdirSync(path.join(tmpDir, "subdir")); | ||
| fs.writeFileSync(path.join(tmpDir, "subdir", "nested.txt"), "hello"); | ||
| fs.mkdirSync(path.join(tmpDir, "emptydir")); | ||
| fs.mkdirSync(path.join(tmpDir, ".hiddendir")); | ||
| fs.writeFileSync(path.join(tmpDir, ".hiddendir", "inside.txt"), "x"); | ||
| }); | ||
|
|
||
| afterAll(() => { | ||
| fs.rmSync(tmpDir, { recursive: true, force: true }); | ||
| }); | ||
|
|
||
| function collect(dir: string, options?: Parameters<typeof turbowalk>[2]): Promise<IEntry[]> { | ||
| const all: IEntry[] = []; | ||
| return turbowalk( | ||
| dir, | ||
| (entries) => { | ||
| all.push(...entries); | ||
| }, | ||
| options, | ||
| ).then(() => all); | ||
| } | ||
|
|
||
| describe("turbowalk", () => { | ||
| it("walks all files and directories recursively", async () => { | ||
| const entries = await collect(tmpDir, { skipHidden: false }); | ||
| const names = entries.map((e) => path.basename(e.filePath)).sort(); | ||
| expect(names).toEqual([ | ||
| ".hidden", | ||
| ".hiddendir", | ||
| "emptydir", | ||
| "file1.txt", | ||
| "file2.dat", | ||
| "inside.txt", | ||
| "nested.txt", | ||
| "subdir", | ||
| ]); | ||
| }); | ||
|
|
||
| it("skips hidden files and directories by default", async () => { | ||
| const entries = await collect(tmpDir); | ||
| const names = entries.map((e) => path.basename(e.filePath)).sort(); | ||
| // .hidden, .hiddendir, and inside.txt (inside .hiddendir) should be excluded | ||
| expect(names).toEqual(["emptydir", "file1.txt", "file2.dat", "nested.txt", "subdir"]); | ||
| }); | ||
|
|
||
| it("reports correct isDirectory flag", async () => { | ||
| const entries = await collect(tmpDir, { skipHidden: false }); | ||
| const dirs = entries | ||
| .filter((e) => e.isDirectory) | ||
| .map((e) => path.basename(e.filePath)) | ||
| .sort(); | ||
| const files = entries | ||
| .filter((e) => !e.isDirectory) | ||
| .map((e) => path.basename(e.filePath)) | ||
| .sort(); | ||
| expect(dirs).toEqual([".hiddendir", "emptydir", "subdir"]); | ||
| expect(files).toEqual([".hidden", "file1.txt", "file2.dat", "inside.txt", "nested.txt"]); | ||
| }); | ||
|
|
||
| it("reports correct file sizes", async () => { | ||
| const entries = await collect(tmpDir, { skipHidden: false }); | ||
| const file1 = entries.find((e) => path.basename(e.filePath) === "file1.txt"); | ||
| const file2 = entries.find((e) => path.basename(e.filePath) === "file2.dat"); | ||
| expect(file1?.size).toBe(10); | ||
| expect(file2?.size).toBe(20); | ||
| }); | ||
|
|
||
| it("reports mtime as unix seconds", async () => { | ||
| const entries = await collect(tmpDir, { skipHidden: false }); | ||
| const file1 = entries.find((e) => path.basename(e.filePath) === "file1.txt"); | ||
| expect(file1?.mtime).toBeGreaterThan(0); | ||
| // Should be within a few seconds of now | ||
| const now = Math.floor(Date.now() / 1000); | ||
| expect(file1!.mtime).toBeGreaterThan(now - 60); | ||
| expect(file1!.mtime).toBeLessThanOrEqual(now + 1); | ||
| }); | ||
|
|
||
| it("respects recurse: false", async () => { | ||
| const entries = await collect(tmpDir, { recurse: false, skipHidden: false }); | ||
| const names = entries.map((e) => path.basename(e.filePath)).sort(); | ||
| // Should only include top-level entries, not nested.txt or inside.txt | ||
| expect(names).toEqual([ | ||
| ".hidden", | ||
| ".hiddendir", | ||
| "emptydir", | ||
| "file1.txt", | ||
| "file2.dat", | ||
| "subdir", | ||
| ]); | ||
| }); | ||
|
|
||
| it("handles non-existent directory gracefully", async () => { | ||
| const entries = await collect(path.join(tmpDir, "nonexistent")); | ||
| expect(entries).toEqual([]); | ||
| }); | ||
|
|
||
| it("calls progress callback with batched entries", async () => { | ||
| const calls: number[] = []; | ||
| await turbowalk( | ||
| tmpDir, | ||
| (entries) => { | ||
| calls.push(entries.length); | ||
| }, | ||
| { skipHidden: false }, | ||
| ); | ||
| // At least one call should have been made | ||
| expect(calls.length).toBeGreaterThan(0); | ||
| // Total entries across all calls should match | ||
| const total = calls.reduce((a, b) => a + b, 0); | ||
| expect(total).toBe(8); // 5 files + 3 dirs | ||
| }); | ||
|
|
||
| it("returns full absolute paths", async () => { | ||
| const entries = await collect(tmpDir, { skipHidden: false }); | ||
| for (const entry of entries) { | ||
| expect(path.isAbsolute(entry.filePath)).toBe(true); | ||
| expect(entry.filePath.startsWith(tmpDir)).toBe(true); | ||
| } | ||
| }); | ||
| }); |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.