Skip to content

feat: Expand property access in externals - #2425

Merged
aleksanderkatan merged 98 commits into
mainfrom
feat/expand-property-access-in-externals
Jul 13, 2026
Merged

aleksanderkatan merged 98 commits into
mainfrom
feat/expand-property-access-in-externals

Conversation

@aleksanderkatan

@aleksanderkatan aleksanderkatan commented Apr 29, 2026 •

Copy link
Copy Markdown
Member

Closes #2504
Closes #2019

See the new doc for description on how now externals work.

The cut-off at '.$' actually changes some behavior: with .$ in externals, number slots resolve to abstract numbers instead of concrete.

@aleksanderkatan aleksanderkatan linked an issue Apr 29, 2026 that may be closed by this pull request
@github-actions

github-actions Bot commented Apr 29, 2026 •

Copy link
Copy Markdown

pkg.pr.new

packages
Ready to be installed by your favorite package manager ⬇️

https://pkg.pr.new/software-mansion/TypeGPU/tgpu-gen@17fd307b04ad12561c0420dd6d95d420d989383b
https://pkg.pr.new/software-mansion/TypeGPU/tinyest-for-wgsl@17fd307b04ad12561c0420dd6d95d420d989383b
https://pkg.pr.new/software-mansion/TypeGPU/typegpu@17fd307b04ad12561c0420dd6d95d420d989383b
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/cli@17fd307b04ad12561c0420dd6d95d420d989383b
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/color@17fd307b04ad12561c0420dd6d95d420d989383b
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/gl@17fd307b04ad12561c0420dd6d95d420d989383b
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/noise@17fd307b04ad12561c0420dd6d95d420d989383b
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/radiance-cascades@17fd307b04ad12561c0420dd6d95d420d989383b
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/react@17fd307b04ad12561c0420dd6d95d420d989383b
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/sdf@17fd307b04ad12561c0420dd6d95d420d989383b
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/three@17fd307b04ad12561c0420dd6d95d420d989383b
https://pkg.pr.new/software-mansion/TypeGPU/unplugin-typegpu@17fd307b04ad12561c0420dd6d95d420d989383b

benchmark
view benchmark

commit
view commit

@github-actions

github-actions Bot commented Apr 29, 2026 •

Copy link
Copy Markdown

Bundle size comparison (import * as ... in PR vs import * as ... in target):

🟢 Decreased (max -8.75%) ➖ Unchanged 🔴 Increased (max 0.00%) ❔ Unknown
3 297 21 0

import * as ... in PR vs import * as ... in target (did bundle size increase?):

Test tsdown
STATIC_useGpuFunction.ts 240 B ($${\color{green}-8.7\%}$$)

import { ... } in PR vs import * as ... in PR (is the library tree-Shakeable?):

Test tsdown
tgpu_init.ts 255.88 kB ($${\color{green}-3.2\%}$$)
tgpu_initFromDevice.ts 255.37 kB ($${\color{green}-3.4\%}$$)
tgpu_resolve.ts 161.34 kB ($${\color{green}-38.9\%}$$)
tgpu_resolveWithContext.ts 161.28 kB ($${\color{green}-39.0\%}$$)
tgpu_bindGroupLayout.ts 68.92 kB ($${\color{green}-73.9\%}$$)
tgpu_mutableAccessor.ts 65.91 kB ($${\color{green}-75.1\%}$$)
tgpu_accessor.ts 65.90 kB ($${\color{green}-75.1\%}$$)
tgpu_privateVar.ts 65.27 kB ($${\color{green}-75.3\%}$$)
tgpu_workgroupVar.ts 65.27 kB ($${\color{green}-75.3\%}$$)
tgpu_const.ts 64.52 kB ($${\color{green}-75.6\%}$$)
tgpu_fn.ts 37.98 kB ($${\color{green}-85.6\%}$$)
tgpu_fragmentFn.ts 37.88 kB ($${\color{green}-85.7\%}$$)
tgpu_vertexFn.ts 37.70 kB ($${\color{green}-85.7\%}$$)
tgpu_computeFn.ts 37.40 kB ($${\color{green}-85.8\%}$$)
tgpu_vertexLayout.ts 26.63 kB ($${\color{green}-89.9\%}$$)
tgpu_comptime.ts 14.43 kB ($${\color{green}-94.5\%}$$)
tgpu_unroll.ts 2.60 kB ($${\color{green}-99.0\%}$$)
tgpu_slot.ts 1.54 kB ($${\color{green}-99.4\%}$$)
tgpu_lazy.ts 1.19 kB ($${\color{green}-99.5\%}$$)

If you wish to run a comparison for other, slower bundlers, run the 'Tree-shake test' from the GitHub Actions menu.

@github-actions

github-actions Bot commented Apr 29, 2026 •

Copy link
Copy Markdown

Resolution Time Benchmark

---
config:
  themeVariables:
    xyChart:
      plotColorPalette: "#E63946, #3B82F6, #059669"
---
xychart
  title "Random Branching (🔴 PR | 🔵 main | 🟢 release)"
  x-axis "max depth" [1, 2, 3, 4, 5, 6, 7, 8]
  y-axis "time (ms)"
  line [0.65, 1.38, 2.95, 4.25, 4.69, 7.98, 14.93, 15.28]
  line [0.70, 1.32, 2.84, 4.54, 5.29, 7.63, 15.23, 17.43]
  line [0.72, 1.44, 3.05, 4.52, 5.16, 8.70, 15.91, 16.40]
Loading
---
config:
  themeVariables:
    xyChart:
      plotColorPalette: "#E63946, #3B82F6, #059669"
---
xychart
  title "Linear Recursion (🔴 PR | 🔵 main | 🟢 release)"
  x-axis "max depth" [1, 2, 3, 4, 5, 6, 7, 8]
  y-axis "time (ms)"
  line [0.20, 0.40, 0.46, 0.58, 0.79, 0.85, 0.98, 1.11]
  line [0.25, 0.41, 0.51, 0.62, 0.86, 0.89, 1.03, 1.14]
  line [0.25, 0.44, 0.49, 0.63, 0.87, 0.86, 1.07, 1.21]
Loading
---
config:
  themeVariables:
    xyChart:
      plotColorPalette: "#E63946, #3B82F6, #059669"
---
xychart
  title "Full Tree (🔴 PR | 🔵 main | 🟢 release)"
  x-axis "max depth" [1, 2, 3, 4, 5, 6, 7, 8]
  y-axis "time (ms)"
  line [0.65, 1.70, 2.70, 4.81, 8.41, 18.85, 37.24, 77.58]
  line [0.69, 1.56, 2.96, 4.82, 9.09, 18.31, 38.92, 78.70]
  line [0.66, 1.58, 2.83, 5.77, 9.57, 18.99, 38.69, 79.44]
Loading

@aleksanderkatan
aleksanderkatan force-pushed the feat/expand-property-access-in-externals branch from e29acd4 to d6e4f7e Compare May 28, 2026 11:47
@aleksanderkatan
aleksanderkatan requested a review from Copilot June 8, 2026 09:52

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes — moved the visited-node optimization set from a module-level WeakMap<Context, Set<JsNode>> into the per-transpilation Context.

  • packages/tinyest-for-wgsl/src/types.ts — added visitedNodes: Set<JsNode> to Context.
  • packages/tinyest-for-wgsl/src/parsers.ts — initializes visitedNodes in both transpileFn and transpileNode contexts.
  • packages/tinyest-for-wgsl/src/externals.ts — removed the module-level WeakMap and reads ctx.visitedNodes directly.

Ran pnpm vitest run --project=!browser packages/tinyest-for-wgsl/tests/parsers.test.ts and pnpm --filter tinyest-for-wgsl test:types; both pass.

Pullfrog  | View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏

@cieplypolar cieplypolar left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❤️ ❤️

Comment thread packages/typegpu/src/resolutionCtx.ts Resolved
aleksanderkatan and others added 2 commits July 8, 2026 14:11
Co-authored-by: Szymon Szulc <103948576+cieplypolar@users.noreply.github.com>

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes — switched auto-naming for flattened external chains from the leaf segment to the full chain with underscores, and applied a few doc wording fixes since the last Pullfrog review.

  • Full-chain underscore auto-naming in packages/typegpu/src/core/resolve/externals.ts and packages/typegpu/src/resolutionCtx.ts. A flattened chain like EXT.myConst now primes the WGSL identifier as EXT_myConst, so distinct chains with the same leaf no longer collide unless they normalize to the same string.
  • Snapshot updates in packages/typegpu/tests/rawFn.test.ts and packages/typegpu/tests/tgslFn.test.ts reflect the new identifiers.
  • Doc wording fixes in apps/typegpu-docs/src/content/docs/advanced/explaining-the-magic.mdx corrected typos and updated the operator-overloading reference.

Pullfrog  | View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏

@reczkok reczkok left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, great docs! Left some nits


function externalsToString(externals: Externals): string {
const entries = Array.from(externals, (key) => `"${key}": () => ${key}`);
return `{ ${entries.join(', ')} }`;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Could we kill the spaces in empty cases?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rest of metadata uses no spaces (ast: {"params":[],"body":[0,[]]},), so I just killed them altogether for consistency

* tryFindExternalChain(ctx, node`local.p.q`); // undefined
* tryFindExternalChain(ctx, node`ext.$.q`); // undefined
*/
export function tryFindExternalChain(ctx: Context, node: JsNode): string | undefined {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For most nodes this will go all the way though right? Couldn't we invert the logic so unless one of the nodes (identifier, this, member) we don't even add it to the Set?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function only traverses down when it actually may be an external chain. See some of my debug below. Do you just want to optimize how many nodes we keep in this set?

Calling tryFindExternalChain...
Visiting node type: BlockStatement
No external chain found.

Calling tryFindExternalChain...
Visiting node type: VariableDeclaration
No external chain found.

Calling tryFindExternalChain...
Visiting node type: MemberExpression
Visiting node type: Identifier
Found external chain: ext.value

Calling tryFindExternalChain...
Visiting node type: VariableDeclaration
No external chain found.

Calling tryFindExternalChain...
Visiting node type: MemberExpression
Visiting node type: MemberExpression
Visiting node type: Identifier
Found external chain: ext.config.multiplier

Calling tryFindExternalChain...
Visiting node type: VariableDeclaration
No external chain found.

Calling tryFindExternalChain...
Visiting node type: MemberExpression
Visiting node type: MemberExpression
Visiting node type: Identifier
Found external chain: ext.config.zero

Calling tryFindExternalChain...
Visiting node type: VariableDeclaration
No external chain found.

Calling tryFindExternalChain...
Visiting node type: MemberExpression
Visiting node type: MemberExpression
Visiting node type: Identifier
Found external chain: ext.config.multiplier

Calling tryFindExternalChain...
Visiting node type: BlockStatement
No external chain found.

Calling tryFindExternalChain...
Visiting node type: VariableDeclaration
No external chain found.

Calling tryFindExternalChain...
Visiting node type: MemberExpression
Visiting node type: Identifier
Found external chain: ext.value

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, we only travel down member expressions, so it would suffice to just keep those (at least until we update externals again)

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes — two fixes from main landed on the branch: the raw-WGSL external resolver was rewritten to walk nested externals via bounded identifier chains, and tinyest-for-wgsl now scopes for/for ... of loop variables so they no longer shadow external names after the loop.

  • Rewrote replaceExternalsInWgsl in packages/typegpu/src/core/resolve/externals.ts to scan bounded identifier/property chains (anyIdent / boundedPropChain) and walk nested ExternalMap values until it reaches a resolvable. Externals are now resolved in lexical order and unused externals are no longer auto-resolved.
  • Scoped for/for ... of heads in packages/tinyest-for-wgsl/src/parsers.ts by pushing/popping a scope around init/test/update/body, preventing a loop-local identifier from being treated as an external in code that follows the loop.
  • Added regression tests for raw-WGSL external chains, for-loop variable shadowing, and updated declare.test.ts plus console-log snapshots to match the new resolution order.
  • Synchronized main and updated the PR-package publishing workflow (pkg-pr.yml) to only publish packages that changed since release.

Pullfrog  | Fix it ➔ | View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏

Comment thread packages/tinyest-for-wgsl/src/types.ts Resolved
@aleksanderkatan

Copy link
Copy Markdown
Member Author

@aleksanderkatan we need to add a note that typescript types are completely ignored in our magic

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ No critical issues — minor suggestions inline.

Reviewed changes — merged main fixes for removing the 'use gpu' directive after transformation and tightening the test helper; updated snapshots and one conversion test.

  • Remove 'use gpu' after transformation in packages/unplugin-typegpu/src/babel.ts and packages/unplugin-typegpu/src/core/factory.ts: the plugin now removes the prologue directive instead of leaving it as a stray string literal, and drops the alreadyTransformed guard because a second pass no longer sees the directive.
  • Refresh snapshots in use-gpu-directive.test.ts, auto-naming.test.ts, and typescript-syntax.test.ts to reflect the directive removal and the current v2 metadata shape.
  • Tighten expectDataTypeOf / expectSnippetOf in packages/typegpu/tests/utils/parseResolved.ts to accept only () => unknown, and update conversion.test.ts to invoke the helper through a 'use gpu' wrapper.

Note: 1 inline comment(s) dropped because they did not anchor to lines inside the PR diff:

  • packages/unplugin-typegpu/src/babel.ts:158 (RIGHT) — line 158 (RIGHT) is not inside a diff hunk

Pullfrog  | Fix it ➔ | View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏

@pullfrog

pullfrog Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

ℹ️ Follow-up on the review above — the inline comment I attempted to leave was dropped by GitHub because the relevant source line is not inside this PR's diff (that code arrived via the merge from main, not through the branch's own changes).

The observation, as a non-anchored note:

In packages/unplugin-typegpu/src/babel.ts, removeUseGpuDirective only removes the first 'use gpu' directive (directives.find(...) + maybeUseGpu?.remove()). If a function body contains duplicate prologue directives, the second one would remain as a stray string literal in the emitted wrapper. The Rollup path already loops over directives and removes all matches, so consider aligning Babel the same way.

All relevant tests pass, so this is a minor edge-case polish rather than a blocker.

Pullfrog  | View workflow run | via Pullfrog | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏

@aleksanderkatan
aleksanderkatan merged commit 8ec6390 into main Jul 13, 2026
8 of 9 checks passed
@aleksanderkatan
aleksanderkatan deleted the feat/expand-property-access-in-externals branch July 13, 2026 13:53
@iwoplaza iwoplaza modified the milestones: next minor, 0.12.0 Jul 27, 2026
@iwoplaza iwoplaza linked an issue Aug 12, 2026 that may be closed by this pull request
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.

chore: Remove externalNames from unplugin impr: Flatten externals feat: Expand property access when generating externals

5 participants