This adds `bench/dev-validation/`, wired as `pnpm bench:dev-validation`,
which measures how much dev-mode Cache Components validation contends
for the dev server's event loop during rapid navigation, and how much
running it on a worker thread relieves that. It toggles
`experimental.devValidationWorker` (added in the previous commit) to A/B
the two configurations on the same build. Until the worker
implementation lands the flag is inert and the A/B shows no delta.
The fixture generates one route per family (`client`, `server`,
`sprite`), each nested several layout segments deep under a `(routes)`
route group. Validation renders a combined payload at every URL depth,
so a deeper route means more validation work per navigation, which
mirrors a realistically deep app rather than a single flat segment. The
runner clicks a family's `<Link>` repeatedly, since navigating to the
current route re-renders and re-validates it on every click. The routes
carry no `instant` config because dev validation applies to page
segments by default at the warning level. The three families isolate the
client prerender, the Flight re-encode plus owner-stack work, and the
Flight payload size, respectively.
The signal is browser-observed TTFB taken from Playwright's own network
timing, because it includes the time a request waits for the event loop
while validation monopolizes it. We deliberately do not use the CLI's
logged request durations: the dev server starts that clock inside the
request handler, after the loop has already yielded to the request, so
the queue wait is invisible to it.
The runner prints each configuration's absolute TTFB (p50/p95/max) side
by side rather than a ratio. The time the worker frees is the validation
render's CPU, which is bounded, route-dependent, and does no IO, so a
ratio would overstate a win that does not scale with total request time.
Because the clicks are back-to-back the numbers are a worst case —
navigations that land inside the validation window — and the `max` tail
is the honest headline: it is the main-thread stall the worker removes.
Added a bunch of stuff to the bench.
---
**New metrics in the HTTP benchmark**
- Report TTFB per route (time to first body byte), next to total
latency.
- Report each route's document size, how many bytes are inline Flight
payload, and the Flight share.
**New script: `pnpm bench:render-pipeline:client`**
- Loads each route in Chrome with tracing and 4x CPU throttling, and
breaks down where client time goes: evaluating chunks, evaluating inline
Flight scripts, compiling, background parsing, GC, time to hydration,
and blocking time before hydration.
- Also prints FCP/LCP/DOMContentLoaded/load from the same trace, and JS
transferred vs parsed.
- Off by default, separate from the timing benchmark, since tracing
perturbs timing.
- Hydration time comes from a small client component added to the
fixture root layout that calls `performance.mark`.
**Bug fixes**
- The benchmark was replacing the fixture's `next.config.js` with an
empty one during runs.
- If the port was already taken, the benchmark could silently measure
whatever server was already running there. Both scripts now refuse to
start if something is already on the port.
- A server that died on startup used to look like a slow server; now it
errors immediately.
- One failed request used to abort the whole run and throw away all
results. Now it costs one sample and gets counted in `errors`.
- Killing an already-dead server used to hang the script.
- Bad flags now error upfront instead of crashing at the end.
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Follow-up to https://github.com/vercel/next.js/pull/95807.
Makes payload size match prod sizes closer and aligns chunk count by
adding a lot of client modules.
I originally thought to unify them into `client.js` or such but maybe
it's good to see how compilation behaves with many small files? Which is
how real projects do that.
```
=== dashboard.html (fixture) vs p-overview.html (real)
total KB 665 637
rows (model/I/T/other) 258/111/3/1 240/125/5/27
byte share model/I/T % 43/57/0 36/62/2
median row size B 906 106
max depth 43 53
mean depth 8.9 10.5
elements per KB(model) 7.7 5.2
pure-data byte share % 18 21
client-ref elements % 35 32
objects:elements ratio 1.18 1.23
avg props per element 2.6 3.1
median children fanout 4 4
string bytes % 50 54
median string len B 12 15
row refs per KB 0.4 0.4
encoded scalars per KB 0.7 1.2
undefined markers 247 740
=== docs.html (fixture) vs nextjs-docs.html (real)
total KB 487 487
rows (model/I/T/other) 72/40/7/1 55/43/2/20
byte share model/I/T % 89/8/3 94/6/0
median row size B 199 340
max depth 27 32
mean depth 22.3 12.3
elements per KB(model) 0.6 0.8
pure-data byte share % 7 9
client-ref elements % 29 33
objects:elements ratio 8.67 4.92
avg props per element 2.0 2.8
median children fanout 3 4
string bytes % 47 64
median string len B 17 35
row refs per KB 0.2 0.1
encoded scalars per KB 7.4 2.9
undefined markers 3586 1377
=== blog.html (fixture) vs vercel-blog.html (real)
total KB 778 797
rows (model/I/T/other) 72/39/0/1 102/53/7/14
byte share model/I/T % 93/7/0 87/8/5
median row size B 145 505
max depth 34 44
mean depth 9.6 12.2
elements per KB(model) 0.7 1.1
pure-data byte share % 92 85
client-ref elements % 38 33
objects:elements ratio 31.19 16.39
avg props per element 1.9 2.7
median children fanout 3 4
string bytes % 46 48
median string len B 6 9
row refs per KB 0.1 0.1
encoded scalars per KB 0.4 0.6
undefined markers 242 426
```
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This adds more fixtures modeled after Flight payloads from real sites:
- Vercel Dashboard-like (very app-y)
- React Docs-like after App Router conversion (mostly MDX site)
- Vercel Blog-like (passing down data from CMS)
(Actual data is randomly generated with a seed)
These are simplifications but I tried to incorporate the originals'
corresponding quirks. Such as Vercel Dashboard having many client
components; syntax highlight with many small spans in React Docs; Vercel
Blog currently shipping a load of data unnecessarily on the index page.
(The last one is not ideal but I think we should actually benchmark
"bad" cases like this too so I kept that.)
### Screenshots
<img width="1325" height="1021" alt="Screenshot 2026-07-15 at 04 29 43"
src="https://github.com/user-attachments/assets/62c80722-6fd5-4884-89db-106345f6412d"
/>
<img width="1360" height="994" alt="Screenshot 2026-07-15 at 04 30 21"
src="https://github.com/user-attachments/assets/0db93cd8-43ca-41df-b3e2-c422e0c94656"
/>
<img width="1368" height="991" alt="Screenshot 2026-07-15 at 04 30 43"
src="https://github.com/user-attachments/assets/9369e9be-12a4-4a1e-af32-01b6fdd4a457"
/>
### Comparison with real payloads
```
=== dashboard.html (fixture) vs p-overview.html (real)
total KB 208 637
rows (model/I/T/other) 186/48/7/1 240/125/5/27
byte share model/I/T % 92/4/4 36/62/2
median row size B 590 106
max depth 28 53
mean depth 8.4 10.5
elements per KB(model) 14.9 5.2
pure-data byte share % 24 21
client-ref elements % 41 32
objects:elements ratio 1.17 1.23
avg props per element 1.7 3.1
median children fanout 4 4
string bytes % 24 54
median string len B 10 15
row refs per KB 0.9 0.4
encoded scalars per KB 3.5 1.2
undefined markers 347 740
=== docs.html (fixture) vs nextjs-docs.html (real)
total KB 261 487
rows (model/I/T/other) 58/15/7/1 55/43/2/20
byte share model/I/T % 93/1/6 94/6/0
median row size B 193 340
max depth 27 32
mean depth 21.9 12.3
elements per KB(model) 0.8 0.8
pure-data byte share % 8 9
client-ref elements % 16 33
objects:elements ratio 6.26 4.92
avg props per element 1.7 2.8
median children fanout 4 4
string bytes % 47 64
median string len B 17 35
row refs per KB 0.2 0.1
encoded scalars per KB 7.5 2.9
undefined markers 1949 1377
=== blog.html (fixture) vs vercel-blog.html (real)
total KB 475 797
rows (model/I/T/other) 41/15/0/1 102/53/7/14
byte share model/I/T % 100/0/0 87/8/5
median row size B 426 505
max depth 34 44
mean depth 9.7 12.2
elements per KB(model) 0.8 1.1
pure-data byte share % 92 85
client-ref elements % 23 33
objects:elements ratio 28.92 16.39
avg props per element 1.9 2.7
median children fanout 3 4
string bytes % 46 48
median string len B 6 9
row refs per KB 0.1 0.1
encoded scalars per KB 0.4 0.6
undefined markers 161 426
```
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
### What?
Add two dynamic routes to the render-pipeline benchmark and include both
in the default route suite:
- `/attributes` renders 1,000 rows with string, data, ARIA, title, and
inline-style attributes.
- `/tailwind` renders a realistic dashboard with dense Tailwind-style
utility class names across navigation, metrics, project cards, and an
activity table.
### Why?
The existing benchmark routes focus on lightweight rendering or Suspense
and Flight payload stress. They do not provide focused integration
coverage for React Fizz attribute serialization.
`/attributes` provides a synthetic stress case modeled after the
upstream React benchmark for facebook/react#36899. `/tailwind`
complements it with a production-like component tree and realistic class
strings, including responsive, state, dark-mode, and arbitrary-value
utilities.
### How?
Both fixtures run through the real Next.js production-server
render-pipeline harness and are forced dynamic so every request
exercises server rendering.
The Tailwind-style fixture contains 18 project cards, 36 activity rows,
and 101 `className` attributes. It intentionally does not install or
configure Tailwind because the benchmark targets React serialization of
the class strings, not generated CSS or visual styling.
The new routes are registered in the default stress suite and documented
in the benchmark playbook and render-pipeline README.
### Verification
- Prettier for all changed files
- ESLint for both benchmark fixtures and
`bench/render-pipeline/benchmark.ts`
- Production `next build` + `next start` smoke benchmark for
`/tailwind`; single-client and under-load phases completed with zero
request errors
- Three interleaved base/head `/attributes` runs with 500 serial and
5,000 loaded requests
- `/attributes` PR-head median throughput: +8.26% single-client and
+10.85% under load
- `/attributes` PR-head median p95: 7.91% better single-client and 9.61%
better under load
- `git diff --check`
<!-- NEXT_JS_LLM_PR -->
### What?
Remove `experimental.useNodeStreams` from the public config surface and
make Node streams always-on for Node.js App Router rendering.
The smaller default-on change and dedicated CI cleanup landed in #94311.
The standalone compatibility update landed separately in #94347.
### Why?
Node streams are now the default Node.js runtime rendering path, so the
opt-in config and runtime selection plumbing are stale. Edge bundles
continue to use the web-stream path because Node streams are unavailable
there.
### How?
- Define `__NEXT_USE_NODE_STREAMS` as true for Node.js app runtime
bundles and false for edge user bundles.
- Remove config, schema, runtime, export, and server plumbing for
`experimental.useNodeStreams`.
- Delete the obsolete env-precedence fixture.
- Clean benchmark docs and scripts that still set or compare
`experimental.useNodeStreams`.
### Verification
- `pnpm --filter=next types`
- `pnpm build-all`
<!-- NEXT_JS_LLM_PR -->
## What
Adds a new "A/B branch comparison" section to `bench/BENCHMARKING.md`
with practical guidance for comparing benchmark results across two
branches.
## Why
From running benchmarks comparing canary vs PR #92678, we identified
several patterns that lead to noisy or misleading results:
- Running the full route suite when only one route shows signal wastes
time (~3 min per run)
- The default 120 serial requests is too noisy for sub-2ms routes
- Single runs can swing 10-15% on light routes
- Percentage deltas from a single pair of runs can be misleading without
absolute numbers
## What's in the new section
- Start with a focused route, not the full suite
- Increase request counts for fast routes (500 serial / 5000 load)
- Run at least 3 times per side to average out noise
- Compare absolute req/s, not just deltas
- Watch for system state drift between runs
- Full example workflow with checkout/build/run loop
<!-- NEXT_JS_LLM_PR -->
### What?
Improves the render pipeline benchmark
(`bench/render-pipeline/benchmark.ts`) for more reliable and accurate
RPS measurement.
### Why?
The benchmark had several issues that affected measurement accuracy:
- V8 CPU profiler was on by default, adding 5-15% overhead to all
numbers
- 30 warmup requests was insufficient for V8 TurboFan JIT stabilization
- No stddev made it impossible to tell signal from noise
- Request errors crashed the whole benchmark instead of being tracked
- No port cleanup delay between mode switches could cause bind failures
- No option to isolate routes from cross-route GC/memory contamination
- Closed-loop measurement model limitations were undocumented
### How?
- Default `--capture-cpu` to `false` (use `--capture-cpu=true` for
dedicated profiling runs)
- Add `--warmup-until-stable` flag (default: true) that runs warmup in
batches and stops when mean latency delta is <5% between consecutive
batches
- Add standard deviation to `computeStats` and print it in results
- Track request errors in the under-load phase instead of aborting
- Add 2s sleep between mode switches when `--stream-mode=both`
- Add `--isolate-routes` flag to restart the server between routes
- Document the closed-loop measurement model in code and README
- Add minimal-server scenario documentation to README and benchmarking
playbook
<!-- NEXT_JS_LLM_PR -->
Reverts vercel/next.js#84389
Attempt number 3, #84374 fixed propagation of bundler environment variables to vercel cli operations, to ensure the test configuration is respected.
[Deployment Tests Run 1](https://github.com/vercel/next.js/actions/runs/18146684793/job/51649631635). A fair number of failures.
[Deployment Tests Run 2](https://github.com/vercel/next.js/actions/runs/18154875270). After #84395. These still have a set of failures, but i confirmed that the deployment builds are running `webpack`.
The tests that are failing are related to 'prefetches' (test/e2e/app-dir/segment-cache/prefetch-runtime/prefetch-runtime.test.ts and test/e2e/app-dir/segment-cache/prefetch-layout-sharing/prefetch-layout-sharing.test.ts) and generally the error is a timeout. Recent deployment runs for canary releases are also failing, but on different tests.
Trying again after #[84419](https://github.com/vercel/next.js/pull/84419). [Deployment Tests Run 3](https://github.com/vercel/next.js/actions/runs/18173375427). Failures appeared flaky, rerunning failures...
Reverts vercel/next.js#84348 which itself reverted #84216.
In the 2nd attempt to make --turbopack a default behavior.
* adjust the Generate Pull Request States action to specify `--webpack`
* set the `IS_WEBPACK_TEST` flag in the deployment e2e tests (which are now [passing](https://github.com/vercel/next.js/actions/runs/18113062122))
* make the warning about having a webpack config without a turbopack config more verbose and consistently an error
### What?
Move persistent caching for `next build` into a separate config option: `turbopackPersistentCachingForBuild`.
And `next dev` to `turbopackPersistentCachingForDev`.
Make Turbopack the default bundler for Next 🚀
## What
Add a `--webpack` flag so users can select webpack
Change behavior so if neither `--turbopack` or `--webpack` is set we default to Turbopack.
If we have defaulted the build to turbopack and we observe that the user has a `webpack` config in their next config but no turbopack config, then we issue a warning about this being a potential problem, if this happens during `next build` the warning becomes an error and fails the build. The solution for users is just to explicitly set `--webpack` or `--turbopack`
There were a number of subtle issues
* some users directly set the `TURBOPACK` environment variable, this PR adds support for that though users should really be passing `--turbopack` so it will not be documented
* rspack is enabled via a plugin that sets an environment variable when loading next config, which means it happens way too late!
* For builds this isn't too bad, we just have to recompute the `Bundler` after loading the config. For `dev` the parent process can get out of sync with the child, but this is only really relevant for telemetry and for that we already load the config in the parent so just defer computing `isTurboSession`.
Most of this is about fixing package.json scripts and CI builds configs.
* For package.json
* i added aliases e.g. `test-dev` == `test-dev-webpack` but preserved the old names. In the long run we will want to remove the unsuffixed aliases
* this required some modifications to existing configs to ensure everything was setting the correct env variables
* For ci, i added a new `IS_WEBPACK_TEST` variable to a number of tests but preserved all names.
* Again, in the future it would make sense to rename ci jobs but that is deferred for right now.
* This also makes it clear that a set of tests scenarios (e.g. ppr, experimental, test-new-tests-*) never run with turbopack. This is not addressed right now but should be in the future.
## Why
Today, the default bundler for next is webpack but with turbopack becoming stable it is time to just ship it. Turbopack is already recommended for dev and builds. Create Next App also steers users towards Turbopack. According to telemetry we already have about 50% of all dev sessions and build adoption is growing. So fundamentally why are we even asking users to make a decision?
# Add automated benchmark runner for module-cost
## What?
This PR adds an automated benchmark runner for the module-cost benchmark, allowing for consistent measurement of module loading and execution times.
## Why?
It is to tedious and error prone to run the benchmark.
## Usage
Build `next` and turbopack however you like
```
pnpm i
pnpm prepare-bench
pnpm build-webpack (or build-turbopack)
pnpm benchmark
```
## How?
v0 mostly
### What?
Add a benchmark that requires/imports nearly 10k of empty modules.
Measures load and execution time for these modules.
Might also be useful to measure the compile overhead of modules.
Closes PACK-4263
This:
- Adds support for using `config.turbopack` for Turbopack. The accepted value for this option is identical to the previous experimental one without the deprecated field `loaders`.
- Deprecates `config.experimental.turbo`. Warns on its use, but will continue to accept it, merging it with `config.turbopack` while preferring fields on `config.turbopack`.
- also see https://github.com/vercel/next.js/pull/77850#pullrequestreview-2749738760
Test Plan:
- [x] Convert use of `config.experimental.turbo` in tests to `config.turbopack`
- [x] Automated test for accepting the experimental option for compatibility, as well as the config merging
Title: Fix typo in .gitignore file: change "commiting" to "committing"
(single "t" to double "t")
Description:
This pull request addresses a minor typo in the .gitignore file, where
the word "commiting" was corrected to "committing" by changing a single
"t" to a double "t". This change ensures that the spelling is consistent
with standard English usage.
Checklist:
I have run pnpm prettier-fix to ensure the code is properly formatted.
I have reviewed the [Docs Contribution
Guide](https://nextjs.org/docs/community/contribution-guide) to ensure
my changes adhere to the guidelines.
Related Issues: None
Notes:
This change does not affect functionality or require additional testing.
No additional documentation is required for this typo fix.
- Fixes https://github.com/vercel/next.js/issues/72378
Co-authored-by: Sam Ko <sam@vercel.com>
Changes `--turbo` -> `--turbopack` to avoid confusion.
- Updated docs
- Updates create-next-app -- For create-next-app it's a rename as
otherwise there is an ordering problem with the prompts
- For the CLI `next dev --turbo` is still supported, will eventually be
a warning in a future version to swap with `--turbopack` but is not a
requirement today, can be handled automatically by the upgrade codemod
- New CLI flag: `next dev --turbopack`
<!-- Thanks for opening a PR! Your contribution is much appreciated.
To make sure your PR is handled as smoothly as possible we request that
you follow the checklist sections below.
Choose the right checklist for the change(s) that you're making:
## For Contributors
### Improving Documentation
- Run `pnpm prettier-fix` to fix formatting issues before opening the
PR.
- Read the Docs Contribution Guide to ensure your contribution follows
the docs guidelines:
https://nextjs.org/docs/community/contribution-guide
### Adding or Updating Examples
- The "examples guidelines" are followed from our contributing doc
https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md
- Make sure the linting passes by running `pnpm build && pnpm lint`. See
https://github.com/vercel/next.js/blob/canary/contributing/repository/linting.md
### Fixing a bug
- Related issues linked using `fixes #number`
- Tests added. See:
https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs
- Errors have a helpful link attached, see
https://github.com/vercel/next.js/blob/canary/contributing.md
### Adding a feature
- Implements an existing feature request or RFC. Make sure the feature
request has been accepted for implementation before opening a PR. (A
discussion must be opened, see
https://github.com/vercel/next.js/discussions/new?category=ideas)
- Related issues/discussions are linked using `fixes #number`
- e2e tests added
(https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs)
- Documentation added
- Telemetry added. In case of a feature if it's used or not.
- Errors have a helpful link attached, see
https://github.com/vercel/next.js/blob/canary/contributing.md
## For Maintainers
- Minimal description (aim for explaining to someone not on the team to
understand the PR)
- When linking to a Slack thread, you might want to share details of the
conclusion
- Link both the Linear (Fixes NEXT-xxx) and the GitHub issues
- Add review comments if necessary to explain to the reviewer the logic
behind a change
### What?
### Why?
### How?
Closes NEXT-
Fixes #
-->
---------
Co-authored-by: Will Binns-Smith <wbinnssmith@gmail.com>
Fairly basic PR that just fixes the `basic-app` benchmark application.
The api route was throwing an error since it wasn't returning a
`Response`, and renamed some `pages/` directory routes so they become
available.
Since we reset the test project on every e2e CI run, deployment protection is automatically enabled by default.
This adds an option to the reset project workflow to disable deployment protection. Our test runners need to be able to hit these pages from an unauthenticated browser in order for the tests to work.
Verified tests are running properly in [this run](https://github.com/vercel/next.js/actions/runs/6971348806/job/18971225559) (fixing any failing tests themselves are out of scope for this PR; will evaluate once the run finishes)
Closes NEXT-1732
We have identical `resetProject` code used in `bench/vercel` and our e2e workflow action -- this updates the `resetProject` script to side-effects free (hence removing the env var) and shared between bench & e2e
Closes NEXT-1731
## What?
In Next, rendering a route involves 3 layers:
- the routing layer, which will direct the request to the correct route to render
- the rendering layer, which will take a route and render it appropriately
- the user layer, which contains the user code
In #51831, in order to optimise the boot time of Next.js, I introduced a change that allowed the routing layer to be bundled. In this PR, I'm doing the same for the rendering layer. This is building up on @wyattjoh's work that initially split the routing and the rendering layer into separate entry-points.
The benefits of having this approach is that this allows us to compartmentalise the different part of Next, optimise them individually and making sure that serving a request is as efficient as possible, e.g. rendering a `pages` route should not need code from the `app router` to be used.
There are now 4 different rendering runtimes, depending on the route type:
- app pages: for App Router pages
- app routes: for App Router route handlers
- pages: for legacy pages
- pages api: for legacy API routes
This change should be transparent to the end user, beside faster cold boots.
## Notable changes
Doing this change required a lot of changes for Next.js under the hood in order to make the different layers play well together.
### New conventions for externals/shared modules
The big issue of bundling the rendering runtimes is that the user code needs to be able to reference an instance of a module/value created in Next during the render. This is the case when the user wants to access the router context during SSR via `next/link` for example; when you call `useContext(value)` the value needs to be the exact same reference to one as the one created by `createContext` earlier.
Previously, we were handling this case by making all files from Next that were affected by this `externals`, meaning that we were marking them not to be bundled.
**Why not keep it this way?**
The goal of this PR as stated previously was to make the rendering process as efficient as possible, so I really wanted to avoid extraneous fs reads to unoptimised code.
In order to "fix" it, I introduced two new conventions to the codebase:
- all files that explicitly need to be shared between a rendering runtime and the user code must be suffixed by `.shared-runtime` and exposed via adding a reference in the relevant `externals` file. At compilation time, a reference to a file ending with this will get re-written to the appropriate runtime.
- all files that need to be truly externals need to be suffixed by `.external`. At compilation time, a reference to it will stay as-is. This special case is needed mostly only for the async local storages that need to be shared with all three layers of Next.
As a side effect, we should be bundling more of the Next code in the user bundles, so it should be slightly more efficient.
### App route handlers are compiled on their own layer
App route handlers should be compiled in their own layer, this allows us to separate more cleanly the compilation logic here (we don't need to run the RSC logic for example).
### New rendering bundles
We now generate a prod and a dev bundle for:
- the routing server
- the app/pages SSR rendering process
- the API routes process
The development bundle is needed because:
- there is code in Next that relies on NODE_ENV
- because we opt out of the logic referencing the correct rendering runtime in dev for a `shared-runtime` file. This is because we don't need to and that Turbopack does not support rewriting an external to something that looks like this `require('foo').bar.baz` yet. We will need to fix that when Turbopack build ships.
### New development pipeline
Bundling Next is now required when developing on the repo so I extended the taskfile setup to account for that. The webpack config for Next itself lives in `webpack.config.js` and contains the logic for all the new bundles generated.
### Misc changes
There are some misc reshuffling in the code to better use the tree shaking abilities that we can now use.
fixes NEXT-1573
Co-authored-by: Alex Kirszenberg <1621758+alexkirsz@users.noreply.github.com>
This replaces the existing recursive directory reading beheviour with a more efficient implementation. Rather than previously doing actual recursion with the function calls to go deeper into each directory, this has been rewritten to instead use a while loop and a stack. This should improve memory usage for projects with very deep directory structures.
The updated design that performs directory scanning on all directories found at each layer, allowing more filesystem calls to be sent to the OS instead of waiting like the previous implementation did.
**Currently the new implementation is about 3x faster.**