@effectionx/inline
v0.0.1
Published
Optimize deeply nested operations by collapsing yield delegation into a single iterator
Downloads
27
Readme
🚀 Inline
Collapse nested yield* delegation into a single flat iterator for
performance-critical code paths.
On rare occasions, yield* syntax can cause a performance
degradation, for example when there are many, many levels of
recursion. The reason is because when javascript composes generators via yield*, each level
of nesting creates an intermediate generator frame. The
engine must unwind through every one of these frames on each call to
.next(), .return(), or .throw(). For deeply nested or recursive
operations, the cost of resuming a single yield point is O(depth).
Most of the time, this overhead is negligible and you should use plain
yield* — it's type-safe, gives you clear stack traces, and composes
naturally. But if you've profiled your code and identified deep
yield* nesting as a bottleneck (e.g. recursive operations or tight
inner loops with many layers of delegation), inline() lets you
opt into a flat execution model where the cost is O(1) regardless of
depth.
Instead of delegating with yield*:
let value = yield* someOperation();Use inline() with a plain yield:
import { inline } from "@effectionx/inline";
let value = (yield inline(someOperation())) as SomeType;The trade-off is that the return type is unknown (requiring a cast),
and you lose the natural generator stack trace.
Note: Source map support for the build-time transforms is not yet available but is planned for a future release.
Build-time Transform
Instead of manually converting each yield* call, you can apply the
inline optimization automatically at build time. The transform rewrites
every yield* expression inside generator functions into the equivalent
yield inline(...) call. This means you can benefit from type-safety
and helpful stack traces while developing, but ship optimal code to
production.
esbuild
import { build } from "esbuild";
import { inlinePlugin } from "@effectionx/inline/esbuild";
await build({
entryPoints: ["src/index.ts"],
bundle: true,
plugins: [inlinePlugin()],
});SWC
A compiled WASM plugin is available for use with @swc/core or any
SWC-based toolchain (e.g. Next.js, Parcel):
import { transformSync } from "@swc/core";
let result = transformSync(source, {
jsc: {
experimental: {
plugins: [["@effectionx/inline/swc", {}]],
},
},
});Both transforms produce identical output: they add
import { inline as $$inline } from "@effectionx/inline" and rewrite
yield* expr() into (yield $$inline(expr())).
You can opt out of the transform for specific functions with a
/** @noinline */ JSDoc annotation, or for an entire file by adding
"no inline"; as the first statement.
