Skip to content

perf: skip splitting the source when there is no return to unwrap - #37

Open
vast wants to merge 1 commit into
pugjs:masterfrom
vast:claude/faster-with-no-returns
Open

vast wants to merge 1 commit into
pugjs:masterfrom
vast:claude/faster-with-no-returns

Conversation

@vast

@vast vast commented Oct 8, 2026

Copy link
Copy Markdown

unwrapReturns splits the whole source into an array of characters and joins it back on every call, even when there is no return outside a function to replace. For large sources this copy is a noticeable share of addWith. Pug calls addWith on the entire generated code of a template, which never has a top-level return, so every Pug compile pays for it.

This PR creates the array lazily, on the first replacement, and returns the source as is when nothing was replaced. When there is a return to unwrap, the code path is exactly the same as before.

Output is unchanged. src.split('').join('') is src, so skipping both gives the same string. To check it in practice I ran the released 7.0.2 and this branch side by side and compared the returned strings (and the thrown errors) byte for byte:

  • 1,372 real Pug template bodies (up to 446 KB) with several exclude lists and object arguments, plus the same bodies with return statements added at the start, middle and end;
  • 5,000 .js files from a node_modules tree, as is and with a top-level return appended;
  • 112 hand-written sources × 12 object arguments × 6 exclude lists, covering return at top level, inside if, loops, switch, try, and inside nested functions, arrow functions, class and object methods, with non-ASCII and astral characters around the replaced ranges;
  • 5,000 randomly generated programs mixing the above.

38,256 calls in total, with both @babel/parser 7.9.6 (the lockfile version) and 7.29.7: no differences. Existing tests, lint and prettier pass.

Timing. addWith('locals || {}', src, exclude) on code generated by Pug; median of 30 runs per version after 10 warm-up runs, versions alternating; Node 22.21.1, @babel/parser 7.29.7, Apple M4:

Source 7.0.2 This PR
446 KB template 35.0 ms 23.9 ms
389 KB template 28.7 ms 22.1 ms
124 KB template 9.7 ms 7.3 ms
446 KB template with a top-level return 33.5 ms 33.5 ms

No public API, dependency or parser changes.

unwrapReturns split the whole source into an array of characters and
joined it back on every call, even when no return statement outside a
function had to be replaced. For large sources that copy costs more than
everything else addWith does after parsing: on a 389 KB template body
generated by Pug it took about 7 ms of 29 ms.

Create the array on the first replacement instead and return the source
as is when there was none. The output does not change: joining an
untouched split('') gives the original string back.
@rollingversions

rollingversions Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

This PR will not result in a new version of with as there are no user facing changes.

Add changes to trigger a release

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant