Repository: software-mansion/TypeGPU
Pull request #1344 (2025-06-09 12:02:24)

feat: Automatically assign names to resources in Unplugin
The high-level rule for autonaming is like this: if there is an assignment to a variable, we try to find one of the `resourceConstructors` among the identifiers in the assigned expression; if we succeed (and we don't find the `$name` identifier), we wrap the expression in a call to `__TYPEGPU_AUTONAME__`. If typegpu is initialized, this function checks if the expression has the `$internal` symbol, and names it (using `$name`) if it isn't already named.

I also needed to add `[$internal]: true` to some of the resources.

Let me know if I missed any of the resource generating functions.

github-actions[bot]: **pkg.pr.new**

*packages*
```
pnpm i https://pkg.pr.new/software-mansion/TypeGPU/typegpu@1344
pnpm i https://pkg.pr.new/software-mansion/TypeGPU/typegpu@8a500c085e0c1bf12a368314be150b3ad3cf0ad8
```

*benchmark*
[view benchmark](https://docs.swmansion.com/TypeGPU/benchmark?p=npm-latest_pr-1344)

*commit*
[view commit](https://github.com/software-mansion/TypeGPU/commit/8a500c085e0c1bf12a368314be150b3ad3cf0ad8)
aleksanderkatan: At least +500, -100 of these changes are tests :)

The high-level rule for autonaming is like this: if there is an assignment to a variable, we try to find one of the `resourceConstructors` among the identifiers in the assigned expression; if we succeed (and we don't find the `$name` identifier), we wrap the expression in a call to `__TYPEGPU_AUTONAME__`. If typegpu is initialized, this function checks if the expression has the `$internal` symbol, and names it if it isn't already named.

I also needed to add `[$internal]: true` to some of the resources.

Let me know if I missed any of the resource generating functions.
aleksanderkatan: As of right now, functions marked with our directives are not assigned names (no `$name` method, we need to save the names in the metadata). I decided to delegate that in another ticket: #1358
reczkok: > At least +500, -100 of these changes are tests :)
> 
> The high-level rule for autonaming is like this: if there is an assignment to a variable, we try to find one of the `resourceConstructors` among the identifiers in the assigned expression; if we succeed (and we don't find the `$name` identifier), we wrap the expression in a call to `__TYPEGPU_AUTONAME__`. If typegpu is initialized, this function checks if the expression has the `$internal` symbol, and names it (using `$name`) if it isn't already named.
> 
> I also needed to add `[$internal]: true` to some of the resources.
> 
> Let me know if I missed any of the resource generating functions.

Please add that to the PR description

Patch:
diff --git a/packages/typegpu/src/core/buffer/buffer.ts b/packages/typegpu/src/core/buffer/buffer.ts
index d07af0ae..086641a2 100644
--- a/packages/typegpu/src/core/buffer/buffer.ts
+++ b/packages/typegpu/src/core/buffer/buffer.ts
@@ -13,6 +13,7 @@ import type { StorageFlag } from '../../extension.ts';
 import type { TgpuNamable } from '../../shared/meta.ts';
 import { getName, setName } from '../../shared/meta.ts';
 import type { Infer, InferPartial, MemIdentity } from '../../shared/repr.ts';
+import { $internal } from '../../shared/symbols.ts';
 import type { UnionToIntersection } from '../../shared/utilityTypes.ts';
 import { isGPUBuffer } from '../../types.ts';
 import type { ExperimentalTgpuRoot } from '../root/rootTypes.ts';
@@ -72,6 +73,7 @@ const usageToUsageConstructor = {
 };
 
 export interface TgpuBuffer<TData extends BaseData> extends TgpuNamable {
+  readonly [$internal]: true;
   readonly resourceType: 'buffer';
   readonly dataType: TData;
   readonly initial?: Infer<TData> | undefined;
@@ -135,6 +137,7 @@ type RestrictVertexUsages<TData extends BaseData> = TData extends {
   : 'vertex'[];
 
 class TgpuBufferImpl<TData extends AnyData> implements TgpuBuffer<TData> {
+  public readonly [$internal] = true;
   public readonly resourceType = 'buffer';
   public flags: GPUBufferUsageFlags = GPUBufferUsage.COPY_DST |
     GPUBufferUsage.COPY_SRC;
diff --git a/packages/typegpu/src/core/function/tgpuComputeFn.ts b/packages/typegpu/src/core/function/tgpuComputeFn.ts
index 5a7b2d1b..b1868a29 100644
--- a/packages/typegpu/src/core/function/tgpuComputeFn.ts
+++ b/packages/typegpu/src/core/function/tgpuComputeFn.ts
@@ -6,7 +6,7 @@ import {
   setName,
   type TgpuNamable,
 } from '../../shared/meta.ts';
-import { $getNameForward } from '../../shared/symbols.ts';
+import { $getNameForward, $internal } from '../../shared/symbols.ts';
 import type { ResolutionCtx, SelfResolvable } from '../../types.ts';
 import { createFnCore, type FnCore } from './fnCore.ts';
 import type { Implementation, InferIO, IORecord } from './fnTypes.ts';
@@ -74,6 +74,7 @@ export interface TgpuComputeFn<
   // biome-ignore lint/suspicious/noExplicitAny: to allow assigning any compute fn to TgpuComputeFn (non-generic) type
   ComputeIn extends IORecord<AnyComputeBuiltin> = any,
 > extends TgpuNamable {
+  readonly [$internal]: true;
   readonly shell: TgpuComputeFnShellHeader<ComputeIn>;
 
   $uses(dependencyMap: Record<string, unknown>): this;
@@ -148,6 +149,7 @@ function createComputeFn<ComputeIn extends IORecord<AnyComputeBuiltin>>(
   implementation: Implementation,
 ): TgpuComputeFn<ComputeIn> {
   type This = TgpuComputeFn<ComputeIn> & SelfResolvable & {
+    [$internal]: true;
     [$getNameForward]: FnCore;
   };
 
@@ -162,6 +164,7 @@ function createComputeFn<ComputeIn extends IORecord<AnyComputeBuiltin>>(
       return this;
     },
 
+    [$internal]: true,
     [$getNameForward]: core,
     $name(newLabel: string): This {
       setName(core, newLabel);
diff --git a/packages/typegpu/src/core/function/tgpuFragmentFn.ts b/packages/typegpu/src/core/function/tgpuFragmentFn.ts
index cb25c24e..76e06c6f 100644
--- a/packages/typegpu/src/core/function/tgpuFragmentFn.ts
+++ b/packages/typegpu/src/core/function/tgpuFragmentFn.ts
@@ -15,7 +15,7 @@ import {
   setName,
   type TgpuNamable,
 } from '../../shared/meta.ts';
-import { $getNameForward } from '../../shared/symbols.ts';
+import { $getNameForward, $internal } from '../../shared/symbols.ts';
 import type { GenerationCtx } from '../../tgsl/generationHelpers.ts';
 import type { ResolutionCtx, SelfResolvable } from '../../types.ts';
 import { addReturnTypeToExternals } from '../resolve/externals.ts';
@@ -108,6 +108,7 @@ export interface TgpuFragmentFn<
   Varying extends FragmentInConstrained = FragmentInConstrained,
   Output extends FragmentOutConstrained = FragmentOutConstrained,
 > extends TgpuNamable {
+  readonly [$internal]: true;
   readonly shell: TgpuFragmentFnShellHeader<Varying, Output>;
   readonly outputType: IOLayoutToSchema<Output>;
 
@@ -179,7 +180,10 @@ function createFragmentFn(
   >,
   implementation: Implementation,
 ): TgpuFragmentFn {
-  type This = TgpuFragmentFn & SelfResolvable & { [$getNameForward]: FnCore };
+  type This = TgpuFragmentFn & SelfResolvable & {
+    [$internal]: true;
+    [$getNameForward]: FnCore;
+  };
 
   const core = createFnCore(shell, implementation);
   const outputType = shell.returnType;
@@ -201,6 +205,7 @@ function createFragmentFn(
       return this;
     },
 
+    [$internal]: true,
     [$getNameForward]: core,
     $name(newLabel: string): This {
       setName(core, newLabel);
diff --git a/packages/typegpu/src/core/function/tgpuVertexFn.ts b/packages/typegpu/src/core/function/tgpuVertexFn.ts
index 3aecf867..bfaf4c02 100644
--- a/packages/typegpu/src/core/function/tgpuVertexFn.ts
+++ b/packages/typegpu/src/core/function/tgpuVertexFn.ts
@@ -10,7 +10,7 @@ import {
   setName,
   type TgpuNamable,
 } from '../../shared/meta.ts';
-import { $getNameForward } from '../../shared/symbols.ts';
+import { $getNameForward, $internal } from '../../shared/symbols.ts';
 import type { GenerationCtx } from '../../tgsl/generationHelpers.ts';
 import type { ResolutionCtx, SelfResolvable } from '../../types.ts';
 import { addReturnTypeToExternals } from '../resolve/externals.ts';
@@ -90,6 +90,7 @@ export interface TgpuVertexFn<
   // biome-ignore lint/suspicious/noExplicitAny: to allow assigning any vertex fn to TgpuVertexFn (non-generic) type
   VertexOut extends VertexOutConstrained = any,
 > extends TgpuNamable {
+  readonly [$internal]: true;
   readonly shell: TgpuVertexFnShellHeader<VertexIn, VertexOut>;
   readonly outputType: IOLayoutToSchema<VertexOut>;
   readonly inputType: IOLayoutToSchema<VertexIn> | undefined;
@@ -167,6 +168,7 @@ function createVertexFn(
     & TgpuVertexFn<VertexInConstrained, VertexOutConstrained>
     & SelfResolvable
     & {
+      [$internal]: true;
       [$getNameForward]: FnCore;
     };
 
@@ -191,6 +193,7 @@ function createVertexFn(
       return this;
     },
 
+    [$internal]: true,
     [$getNameForward]: core,
     $name(newLabel: string): This {
       setName(core, newLabel);
diff --git a/packages/typegpu/src/core/sampler/sampler.ts b/packages/typegpu/src/core/sampler/sampler.ts
index 23f5ba21..4959e7c5 100644
--- a/packages/typegpu/src/core/sampler/sampler.ts
+++ b/packages/typegpu/src/core/sampler/sampler.ts
@@ -114,13 +114,13 @@ export interface TgpuFixedSampler extends TgpuSampler, TgpuNamable {}
 export interface TgpuFixedComparisonSampler
   extends TgpuComparisonSampler, TgpuNamable {}
 
-export function sampler(props: SamplerProps): TgpuSampler {
+export function sampler(props: SamplerProps): TgpuFixedSampler {
   return new TgpuFixedSamplerImpl(props);
 }
 
 export function comparisonSampler(
   props: ComparisonSamplerProps,
-): TgpuComparisonSampler {
+): TgpuFixedComparisonSampler {
   return new TgpuFixedComparisonSamplerImpl(props);
 }
 
diff --git a/packages/typegpu/src/core/slot/accessor.ts b/packages/typegpu/src/core/slot/accessor.ts
index 29224612..b38a81e0 100644
--- a/packages/typegpu/src/core/slot/accessor.ts
+++ b/packages/typegpu/src/core/slot/accessor.ts
@@ -6,6 +6,7 @@ import {
   $getNameForward,
   $gpuRepr,
   $gpuValueOf,
+  $internal,
   $repr,
   $wgslDataType,
 } from '../../shared/symbols.ts';
@@ -37,6 +38,7 @@ export function accessor<T extends AnyWgslData>(
 
 export class TgpuAccessorImpl<T extends AnyWgslData>
   implements TgpuAccessor<T>, SelfResolvable {
+  public readonly [$internal] = true;
   public readonly resourceType = 'accessor';
   public readonly slot: TgpuSlot<
     TgpuFn<() => T> | TgpuBufferUsage<T> | Infer<T>
diff --git a/packages/typegpu/src/core/slot/slot.ts b/packages/typegpu/src/core/slot/slot.ts
index 7c586795..0782186d 100644
--- a/packages/typegpu/src/core/slot/slot.ts
+++ b/packages/typegpu/src/core/slot/slot.ts
@@ -1,7 +1,12 @@
 import { getResolutionCtx } from '../../gpuMode.ts';
 import { getName, setName } from '../../shared/meta.ts';
 import type { Infer, InferGPU } from '../../shared/repr.ts';
-import { $gpuRepr, $gpuValueOf, $repr } from '../../shared/symbols.ts';
+import {
+  $gpuRepr,
+  $gpuValueOf,
+  $internal,
+  $repr,
+} from '../../shared/symbols.ts';
 import type { ResolutionCtx } from '../../types.ts';
 import { getGpuValueRecursively } from '../valueProxyUtils.ts';
 import type { TgpuSlot } from './slotTypes.ts';
@@ -19,6 +24,7 @@ export function slot<T>(defaultValue?: T): TgpuSlot<T> {
 // --------------
 
 class TgpuSlotImpl<T> implements TgpuSlot<T> {
+  public readonly [$internal] = true;
   public readonly resourceType = 'slot';
 
   // Type-tokens, not available at runtime
diff --git a/packages/typegpu/src/core/slot/slotTypes.ts b/packages/typegpu/src/core/slot/slotTypes.ts
index 0e51dd1f..40730ea9 100644
--- a/packages/typegpu/src/core/slot/slotTypes.ts
+++ b/packages/typegpu/src/core/slot/slotTypes.ts
@@ -1,11 +1,17 @@
 import type { AnyData } from '../../data/dataTypes.ts';
 import type { TgpuNamable } from '../../shared/meta.ts';
 import type { Infer, InferGPU } from '../../shared/repr.ts';
-import { $gpuRepr, $providing, $repr } from '../../shared/symbols.ts';
+import {
+  $gpuRepr,
+  $internal,
+  $providing,
+  $repr,
+} from '../../shared/symbols.ts';
 import type { TgpuFn } from '../function/tgpuFn.ts';
 import type { TgpuBufferUsage } from './../buffer/bufferUsage.ts';
 
 export interface TgpuSlot<T> extends TgpuNamable {
+  readonly [$internal]: true;
   readonly resourceType: 'slot';
 
   // Type-tokens, not available at runtime
@@ -43,6 +49,7 @@ export interface TgpuDerived<T> {
 }
 
 export interface TgpuAccessor<T extends AnyData = AnyData> extends TgpuNamable {
+  readonly [$internal]: true;
   readonly resourceType: 'accessor';
 
   // Type-tokens, not available at runtime
diff --git a/packages/typegpu/src/core/vertexLayout/vertexLayout.ts b/packages/typegpu/src/core/vertexLayout/vertexLayout.ts
index 0de31339..86ed9ea9 100644
--- a/packages/typegpu/src/core/vertexLayout/vertexLayout.ts
+++ b/packages/typegpu/src/core/vertexLayout/vertexLayout.ts
@@ -11,6 +11,7 @@ import { isDecorated, isWgslStruct } from '../../data/wgslTypes.ts';
 import { roundUp } from '../../mathUtils.ts';
 import type { TgpuNamable } from '../../shared/meta.ts';
 import { setName } from '../../shared/meta.ts';
+import { $internal } from '../../shared/symbols.ts';
 import {
   kindToDefaultFormatMap,
   type TgpuVertexAttrib,
@@ -29,6 +30,7 @@ import type {
 export interface TgpuVertexLayout<
   TData extends WgslArray | Disarray = WgslArray | Disarray,
 > extends TgpuNamable {
+  readonly [$internal]: true;
   readonly resourceType: 'vertex-layout';
   readonly stride: number;
   readonly stepMode: 'vertex' | 'instance';
@@ -157,6 +159,7 @@ function dataToContainedAttribs<
 
 class TgpuVertexLayoutImpl<TData extends WgslArray | Disarray>
   implements TgpuVertexLayout<TData> {
+  public readonly [$internal] = true;
   public readonly resourceType = 'vertex-layout';
   public readonly stride: number;
   public readonly attrib: ArrayToContainedAttribs<TData>;
diff --git a/packages/typegpu/src/shared/meta.ts b/packages/typegpu/src/shared/meta.ts
index 8a72485c..4750ff93 100644
--- a/packages/typegpu/src/shared/meta.ts
+++ b/packages/typegpu/src/shared/meta.ts
@@ -1,5 +1,5 @@
 import type { Block, FuncParameter } from 'tinyest';
-import { $getNameForward } from './symbols.ts';
+import { $getNameForward, $internal } from './symbols.ts';
 
 export interface MetaData {
   name?: string | undefined;
@@ -14,8 +14,17 @@ export interface MetaData {
 
 interface GlobalWithMeta {
   __TYPEGPU_META__: WeakMap<object, MetaData>;
+  __TYPEGPU_AUTONAME__: <T>(exp: T, label: string) => T;
 }
 
+Object.assign(globalThis, {
+  '__TYPEGPU_AUTONAME__': <T>(exp: T, label: string): T =>
+    isNamable(exp) &&
+      (exp as unknown as { [$internal]: unknown })?.[$internal] && !getName(exp)
+      ? exp.$name(label)
+      : exp,
+});
+
 function isForwarded(value: unknown): value is { [$getNameForward]: unknown } {
   return !!(value as { [$getNameForward]?: unknown })?.[$getNameForward];
 }
diff --git a/packages/typegpu/src/tgpuBindGroupLayout.ts b/packages/typegpu/src/tgpuBindGroupLayout.ts
index 10df4057..830a3cb3 100644
--- a/packages/typegpu/src/tgpuBindGroupLayout.ts
+++ b/packages/typegpu/src/tgpuBindGroupLayout.ts
@@ -57,6 +57,7 @@ import {
 import type { TgpuNamable } from './shared/meta.ts';
 import { getName, setName } from './shared/meta.ts';
 import type { Infer, MemIdentity } from './shared/repr.ts';
+import { $internal } from './shared/symbols.ts';
 import type { Default, Prettify } from './shared/utilityTypes.ts';
 import type { TgpuShaderStage } from './types.ts';
 import type { Unwrapper } from './unwrapper.ts';
@@ -158,6 +159,7 @@ export interface TgpuBindGroupLayout<
     TgpuLayoutEntry | null
   >,
 > extends TgpuNamable {
+  readonly [$internal]: true;
   readonly resourceType: 'bind-group-layout';
   readonly entries: Entries;
   readonly bound: {
@@ -391,6 +393,7 @@ const DEFAULT_READONLY_VISIBILITY: TgpuShaderStage[] = [
 class TgpuBindGroupLayoutImpl<
   Entries extends Record<string, TgpuLayoutEntry | null>,
 > implements TgpuBindGroupLayout<Entries> {
+  public readonly [$internal] = true;
   private _index: number | undefined;
 
   public readonly resourceType = 'bind-group-layout' as const;
diff --git a/packages/unplugin-typegpu/src/babel.ts b/packages/unplugin-typegpu/src/babel.ts
index 4a021824..300f8ef5 100644
--- a/packages/unplugin-typegpu/src/babel.ts
+++ b/packages/unplugin-typegpu/src/babel.ts
@@ -5,6 +5,7 @@ import type * as babel from '@babel/types';
 import { FORMAT_VERSION } from 'tinyest';
 import { transpileFn } from 'tinyest-for-wgsl';
 import {
+  containsResourceConstructorCall,
   type Context,
   embedJSON,
   gatherTgpuAliases,
@@ -105,8 +106,33 @@ function functionToTranspiled(
   );
 }
 
+function wrapInAutoName(
+  node: babel.Expression,
+  name: string,
+) {
+  return types.callExpression(
+    template.expression('globalThis.__TYPEGPU_AUTONAME__ ?? (a => a)', {
+      placeholderPattern: false,
+    })(),
+    [node, types.stringLiteral(name)],
+  );
+}
+
 function functionVisitor(ctx: Context): TraverseOptions {
   return {
+    VariableDeclarator(path) {
+      if (
+        ctx.autoNamingEnabled &&
+        path.node.id.type === 'Identifier' &&
+        path.node.init &&
+        containsResourceConstructorCall(path.node.init, ctx)
+      ) {
+        path.get('init').replaceWith(
+          wrapInAutoName(path.node.init, path.node.id.name),
+        );
+      }
+    },
+
     ImportDeclaration(path) {
       gatherTgpuAliases(path.node, ctx);
     },
@@ -217,6 +243,7 @@ export default function () {
             options?.forceTgpuAlias ? [options.forceTgpuAlias] : [],
           ),
           fileId: id,
+          autoNamingEnabled: options?.autoNamingEnabled ?? true,
         };
 
         path.traverse(functionVisitor(ctx));
diff --git a/packages/unplugin-typegpu/src/common.ts b/packages/unplugin-typegpu/src/common.ts
index abe7c0b8..e7d99244 100644
--- a/packages/unplugin-typegpu/src/common.ts
+++ b/packages/unplugin-typegpu/src/common.ts
@@ -9,6 +9,7 @@ export type Context = {
    */
   tgpuAliases: Set<string>;
   fileId?: string | undefined;
+  autoNamingEnabled: boolean;
 };
 
 export interface Options {
@@ -16,10 +17,12 @@ export interface Options {
   exclude?: FilterPattern;
   enforce?: 'post' | 'pre' | undefined;
   forceTgpuAlias?: string;
+  autoNamingEnabled?: boolean;
 }
 
 export const defaultOptions = {
   include: [/\.m?[jt]sx?$/],
+  autoNamingEnabled: true,
 };
 
 export function embedJSON(jsValue: unknown) {
@@ -104,6 +107,71 @@ export function isShellImplementationCall(
   );
 }
 
+const resourceConstructors: string[] = [
+  // tgpu
+  'bindGroupLayout',
+  'vertexLayout',
+  // tgpu['~unstable']
+  'slot',
+  'accessor',
+  'privateVar',
+  'workgroupVar',
+  'const',
+  ...fnShellFunctionNames,
+  // d
+  'struct',
+  // root
+  'createBuffer',
+  // root['~unstable']
+  'createPipeline',
+  'createMutable',
+  'createReadonly',
+  'createUniform',
+  'createTexture',
+  'sampler',
+  'comparisonSampler',
+];
+
+/**
+ * Checks if `node` should be wrapped in an autoname function.
+ * Since it is mostly for debugging and clean WGSL generation,
+ * some false positives and false negatives are admissible.
+ */
+export function containsResourceConstructorCall(
+  node: acorn.AnyNode | babel.Node,
+  ctx: Context,
+) {
+  if (node.type === 'CallExpression') {
+    if (isShellImplementationCall(node, ctx)) {
+      return true;
+    }
+    // struct({...})
+    if (
+      node.callee.type === 'Identifier' &&
+      resourceConstructors.includes(node.callee.name)
+    ) {
+      return true;
+    }
+    if (node.callee.type === 'MemberExpression') {
+      if (node.callee.property.type === 'Identifier') {
+        // root.createBuffer({...})
+        if (resourceConstructors.includes(node.callee.property.name)) {
+          return true;
+        }
+        if (node.callee.property.name === '$name') {
+          return false;
+        }
+      }
+      // root.createBuffer(d.f32).$usage('storage')
+      return containsResourceConstructorCall(node.callee.object, ctx);
+    }
+  }
+  if (node.type === 'TaggedTemplateExpression') {
+    return containsResourceConstructorCall(node.tag, ctx);
+  }
+  return false;
+}
+
 export const kernelDirectives = ['kernel', 'kernel & js'] as const;
 export type KernelDirective = (typeof kernelDirectives)[number];
 
diff --git a/packages/unplugin-typegpu/src/index.ts b/packages/unplugin-typegpu/src/index.ts
index b7afa8d1..b338af63 100644
--- a/packages/unplugin-typegpu/src/index.ts
+++ b/packages/unplugin-typegpu/src/index.ts
@@ -7,6 +7,7 @@ import { transpileFn } from 'tinyest-for-wgsl';
 import { createUnplugin, type UnpluginInstance } from 'unplugin';
 import babel from './babel.ts';
 import {
+  containsResourceConstructorCall,
   type Context,
   defaultOptions,
   embedJSON,
@@ -53,6 +54,33 @@ function removeKernelDirective(node: FunctionNode) {
   return cloned;
 }
 
+function assignMetadata(
+  magicString: MagicStringAST,
+  node: acorn.AnyNode,
+  metadata: string,
+) {
+  magicString.prependLeft(
+    node.start,
+    '(($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (',
+  ).appendRight(
+    node.end,
+    `), ${metadata}) && $.f)({}))`,
+  );
+}
+
+function wrapInAutoName(
+  magicString: MagicStringAST,
+  node: acorn.Node,
+  name: string,
+) {
+  magicString
+    .prependLeft(
+      node.start,
+      '((globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(',
+    )
+    .appendRight(node.end, `, "${name}"))`);
+}
+
 const typegpu: UnpluginInstance<Options, false> = createUnplugin(
   (rawOptions) => {
     const options = defu(rawOptions, defaultOptions);
@@ -70,6 +98,7 @@ const typegpu: UnpluginInstance<Options, false> = createUnplugin(
               options.forceTgpuAlias ? [options.forceTgpuAlias] : [],
             ),
             fileId: id,
+            autoNamingEnabled: options.autoNamingEnabled,
           };
 
           const ast = this.parse(code, {
@@ -88,6 +117,16 @@ const typegpu: UnpluginInstance<Options, false> = createUnplugin(
             enter(_node, _parent, prop, index) {
               const node = _node as acorn.AnyNode;
 
+              if (
+                ctx.autoNamingEnabled &&
+                node.type === 'VariableDeclarator' &&
+                node.id.type === 'Identifier' &&
+                node.init &&
+                containsResourceConstructorCall(node.init, ctx)
+              ) {
+                wrapInAutoName(magicString, node.init, node.id.name);
+              }
+
               if (node.type === 'ImportDeclaration') {
                 gatherTgpuAliases(node, ctx);
               }
@@ -166,16 +205,11 @@ const typegpu: UnpluginInstance<Options, false> = createUnplugin(
               externals: {${externalNames.join(', ')}},
             }`;
 
-            // Wrap the implementation in a set to `globalThis` to associate the name, AST and externals with the implementation.
-            magicString.appendLeft(
-              def.start,
-              `${isFunctionStatement && name ? `const ${name} = ` : ''}
-              (($) => ((globalThis.__TYPEGPU_META__ ??= new WeakMap()).set(
-                $.f = (`,
-            ).appendRight(
-              def.end,
-              `) , ${metadata}) && $.f))({})`,
-            );
+            assignMetadata(magicString, def, metadata);
+
+            if (isFunctionStatement && name) {
+              magicString.prependLeft(def.start, `const ${name} = `);
+            }
 
             if (removeJsImplementation) {
               magicString.overwriteNode(


Test patch:
diff --git a/packages/typegpu/tests/bindGroupLayout.test.ts b/packages/typegpu/tests/bindGroupLayout.test.ts
index ba7b6755..cb303de0 100644
--- a/packages/typegpu/tests/bindGroupLayout.test.ts
+++ b/packages/typegpu/tests/bindGroupLayout.test.ts
@@ -148,7 +148,7 @@ describe('TgpuBindGroupLayout', () => {
     root.unwrap(bindGroup);
 
     expect(root.device.createBindGroup).toBeCalledWith({
-      label: '<unnamed>',
+      label: 'layout',
       layout: root.unwrap(layout),
       entries: [
         {
@@ -167,7 +167,7 @@ describe('TgpuBindGroupLayout', () => {
     });
 
     expect(root.device.createBindGroupLayout).toBeCalledWith({
-      label: '<unnamed>',
+      label: 'layout',
       entries: [
         {
           binding: 0,
diff --git a/packages/typegpu/tests/buffer.test.ts b/packages/typegpu/tests/buffer.test.ts
index 363de974..4e28729c 100644
--- a/packages/typegpu/tests/buffer.test.ts
+++ b/packages/typegpu/tests/buffer.test.ts
@@ -55,7 +55,7 @@ describe('TgpuBuffer', () => {
 
     root.unwrap(dataBuffer);
     expect(root.device.createBuffer).toBeCalledWith({
-      label: '<unnamed>',
+      label: 'dataBuffer',
       mappedAtCreation: false,
       size: 64,
       usage: global.GPUBufferUsage.UNIFORM |
@@ -146,7 +146,7 @@ describe('TgpuBuffer', () => {
       // First call (raw buffer)
       [
         {
-          label: '<unnamed>',
+          label: 'buffer',
           mappedAtCreation: false,
           size: 12,
           usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC,
diff --git a/packages/typegpu/tests/computePipeline.test.ts b/packages/typegpu/tests/computePipeline.test.ts
index 81b722d8..540c986f 100644
--- a/packages/typegpu/tests/computePipeline.test.ts
+++ b/packages/typegpu/tests/computePipeline.test.ts
@@ -1,4 +1,5 @@
 import { describe, expect, expectTypeOf, vi } from 'vitest';
+import type { TgpuQuerySet } from '../src/core/querySet/querySet.ts';
 import * as d from '../src/data/index.ts';
 import tgpu, {
   MissingBindGroupsError,
@@ -7,7 +8,6 @@ import tgpu, {
 } from '../src/index.ts';
 import { $internal } from '../src/shared/symbols.ts';
 import { it } from './utils/extendedIt.ts';
-import type { TgpuQuerySet } from '../src/core/querySet/querySet.ts';
 
 describe('TgpuComputePipeline', () => {
   it('can be created with a compute entry function', ({ root, device }) => {
@@ -289,7 +289,7 @@ describe('TgpuComputePipeline', () => {
       pipeline.dispatchWorkgroups(1);
 
       expect(commandEncoder.beginComputePass).toHaveBeenCalledWith({
-        label: '<unnamed>',
+        label: 'pipeline',
         timestampWrites: {
           querySet: querySet.querySet,
           beginningOfPassWriteIndex: 1,
@@ -342,7 +342,7 @@ describe('TgpuComputePipeline', () => {
       pipeline2.dispatchWorkgroups(1);
 
       expect(commandEncoder.beginComputePass).toHaveBeenCalledWith({
-        label: '<unnamed>',
+        label: 'pipeline',
         timestampWrites: {
           querySet: querySet.querySet,
           beginningOfPassWriteIndex: 0,
@@ -351,7 +351,7 @@ describe('TgpuComputePipeline', () => {
       });
 
       expect(commandEncoder.beginComputePass).toHaveBeenCalledWith({
-        label: '<unnamed>',
+        label: 'pipeline2',
         timestampWrites: {
           querySet: querySet.querySet,
           beginningOfPassWriteIndex: 2,
@@ -389,7 +389,7 @@ describe('TgpuComputePipeline', () => {
       pipeline.dispatchWorkgroups(1);
 
       expect(commandEncoder.beginComputePass).toHaveBeenCalledWith({
-        label: '<unnamed>',
+        label: 'pipeline',
         timestampWrites: {
           querySet: querySet.querySet,
           beginningOfPassWriteIndex: 3,
@@ -438,7 +438,7 @@ describe('TgpuComputePipeline', () => {
       pipeline.dispatchWorkgroups(1);
 
       expect(commandEncoder.beginComputePass).toHaveBeenCalledWith({
-        label: '<unnamed>',
+        label: 'pipeline',
         timestampWrites: {
           querySet: querySet.querySet,
           beginningOfPassWriteIndex: 2,
diff --git a/packages/typegpu/tests/renderPipeline.test.ts b/packages/typegpu/tests/renderPipeline.test.ts
index 792d3ea3..f9d4b560 100644
--- a/packages/typegpu/tests/renderPipeline.test.ts
+++ b/packages/typegpu/tests/renderPipeline.test.ts
@@ -1,4 +1,5 @@
 import { describe, expect, expectTypeOf, vi } from 'vitest';
+import type { TgpuQuerySet } from '../src/core/querySet/querySet.ts';
 import * as d from '../src/data/index.ts';
 import tgpu, {
   MissingBindGroupsError,
@@ -8,7 +9,6 @@ import tgpu, {
 } from '../src/index.ts';
 import { $internal } from '../src/shared/symbols.ts';
 import { it } from './utils/extendedIt.ts';
-import type { TgpuQuerySet } from '../src/core/querySet/querySet.ts';
 
 describe('Inter-Stage Variables', () => {
   describe('Non-empty vertex output', () => {
@@ -424,7 +424,7 @@ describe('Inter-Stage Variables', () => {
 
       expect(commandEncoder.beginRenderPass).toHaveBeenCalledWith(
         expect.objectContaining({
-          label: '<unnamed>',
+          label: 'pipeline',
           timestampWrites: {
             querySet: querySet.querySet,
             beginningOfPassWriteIndex: 1,
diff --git a/packages/typegpu/tests/resolve.test.ts b/packages/typegpu/tests/resolve.test.ts
index 6e5b6820..eb6de609 100644
--- a/packages/typegpu/tests/resolve.test.ts
+++ b/packages/typegpu/tests/resolve.test.ts
@@ -1,10 +1,10 @@
 import { describe, expect, it } from 'vitest';
 import * as d from '../src/data/index.ts';
 import tgpu from '../src/index.ts';
+import { setName } from '../src/shared/meta.ts';
 import { $wgslDataType } from '../src/shared/symbols.ts';
 import type { ResolutionCtx } from '../src/types.ts';
 import { parse } from './utils/parseResolved.ts';
-import { setName } from '../src/shared/meta.ts';
 
 describe('tgpu resolve', () => {
   it('should resolve an external struct', () => {
@@ -161,7 +161,7 @@ describe('tgpu resolve', () => {
           range: vec2f,
         }
 
-        fn randomTest() -> f32 {
+        fn random() -> f32 {
           var r: Random;
           r.seed = vec2<f32>(3.14, 1.59);
           r.range = vec2<f32>(0.0, 1.0);
@@ -172,7 +172,7 @@ describe('tgpu resolve', () => {
 
         @compute @workgroup_size(1)
         fn main() {
-          var value = randomTest();
+          var value = random();
         }
       `),
     );
diff --git a/packages/typegpu/tests/root.test.ts b/packages/typegpu/tests/root.test.ts
index 2408754a..f3edb8d0 100644
--- a/packages/typegpu/tests/root.test.ts
+++ b/packages/typegpu/tests/root.test.ts
@@ -1,8 +1,8 @@
 import { describe, expect, vi } from 'vitest';
 import * as d from '../src/data/index.ts';
+import { Void } from '../src/data/wgslTypes.ts';
 import tgpu from '../src/index.ts';
 import { it } from './utils/extendedIt.ts';
-import { Void } from '../src/data/wgslTypes.ts';
 
 describe('TgpuRoot', () => {
   describe('.createBuffer', () => {
@@ -14,7 +14,7 @@ describe('TgpuRoot', () => {
       expect(mockBuffer.getMappedRange).not.toBeCalled();
 
       expect(root.device.createBuffer).toBeCalledWith({
-        label: '<unnamed>',
+        label: 'dataBuffer',
         mappedAtCreation: false,
         size: 4,
         usage: global.GPUBufferUsage.UNIFORM |
@@ -33,7 +33,7 @@ describe('TgpuRoot', () => {
       expect(mockBuffer.getMappedRange).toBeCalled();
 
       expect(root.device.createBuffer).toBeCalledWith({
-        label: '<unnamed>',
+        label: 'dataBuffer',
         mappedAtCreation: true,
         size: 12,
         usage: global.GPUBufferUsage.UNIFORM |
@@ -49,7 +49,7 @@ describe('TgpuRoot', () => {
 
       root.unwrap(dataBuffer);
       expect(root.device.createBuffer).toBeCalledWith({
-        label: '<unnamed>',
+        label: 'dataBuffer',
         mappedAtCreation: false,
         size: 12,
         usage: global.GPUBufferUsage.UNIFORM |
diff --git a/packages/typegpu/tests/unplugin/autoname.test.ts b/packages/typegpu/tests/unplugin/autoname.test.ts
new file mode 100644
index 00000000..9e35ff7a
--- /dev/null
+++ b/packages/typegpu/tests/unplugin/autoname.test.ts
@@ -0,0 +1,163 @@
+import { describe, expect } from 'vitest';
+import * as d from '../../src/data/index.ts';
+import { struct } from '../../src/data/index.ts';
+import tgpu from '../../src/index.ts';
+import { getName } from '../../src/shared/meta.ts';
+import { it } from '../utils/extendedIt.ts';
+
+describe('autonaming', () => {
+  it('autonames resources created using tgpu', () => {
+    const myLayout = tgpu.bindGroupLayout({ foo: { uniform: d.vec3f } });
+    const myVertexLayout = tgpu.vertexLayout((n: number) =>
+      d.arrayOf(d.i32, n)
+    );
+
+    expect(getName(myLayout)).toBe('myLayout');
+    expect(getName(myVertexLayout)).toBe('myVertexLayout');
+  });
+
+  it("autonames resources created using tgpu['~unstable']", () => {
+    const mySlot = tgpu['~unstable'].slot<number>();
+    const myAccessor = tgpu['~unstable'].accessor(d.f32);
+    const myPrivateVar = tgpu['~unstable'].privateVar(d.vec2f);
+    const myWorkgroupVar = tgpu['~unstable'].workgroupVar(d.f32);
+    const myConst = tgpu['~unstable'].const(d.f32, 1);
+
+    expect(getName(mySlot)).toBe('mySlot');
+    expect(getName(myAccessor)).toBe('myAccessor');
+    expect(getName(myPrivateVar)).toBe('myPrivateVar');
+    expect(getName(myWorkgroupVar)).toBe('myWorkgroupVar');
+    expect(getName(myConst)).toBe('myConst');
+  });
+
+  it('autonames structs', () => {
+    const myStruct1 = d.struct({ a: d.u32 });
+    const myStruct2 = struct({ a: d.i32 });
+
+    expect(getName(myStruct1)).toBe('myStruct1');
+    expect(getName(myStruct2)).toBe('myStruct2');
+  });
+
+  it('autonames resources created using root', ({ root }) => {
+    const myBuffer = root.createBuffer(d.u32, 2);
+
+    expect(getName(myBuffer)).toBe('myBuffer');
+  });
+
+  it("autonames resources created using root['~unstable']", ({ root }) => {
+    const myPipeline = root['~unstable']
+      .withCompute(
+        tgpu['~unstable'].computeFn({ workgroupSize: [1] })(() => {}),
+      )
+      .createPipeline();
+    const myMutableBuffer = root['~unstable'].createMutable(d.u32);
+    const myReadonlyBuffer = root['~unstable'].createReadonly(d.u32);
+    const myUniformBuffer = root['~unstable'].createUniform(d.u32);
+    const myTexture = root['~unstable'].createTexture({
+      size: [1, 1],
+      format: 'rgba8unorm',
+    });
+    const mySampler = tgpu['~unstable'].sampler({
+      magFilter: 'linear',
+      minFilter: 'linear',
+    });
+    const myComparisonSampler = tgpu['~unstable'].comparisonSampler({
+      compare: 'equal',
+    });
+
+    expect(getName(myPipeline)).toBe('myPipeline');
+    expect(getName(myMutableBuffer)).toBe('myMutableBuffer');
+    expect(getName(myReadonlyBuffer)).toBe('myReadonlyBuffer');
+    expect(getName(myUniformBuffer)).toBe('myUniformBuffer');
+    expect(getName(myTexture)).toBe('myTexture');
+    expect(getName(mySampler)).toBe('mySampler');
+    expect(getName(myComparisonSampler)).toBe('myComparisonSampler');
+  });
+
+  it('autonames when the constructor is hidden behind other methods', ({ root }) => {
+    const myBuffer = root.createBuffer(d.u32)
+      .$usage('storage')
+      .$addFlags(GPUBufferUsage.STORAGE);
+    const Item = d.struct({ a: d.u32 });
+    const myFn = tgpu['~unstable'].fn(
+      [Item],
+      Item,
+    ) /* wgsl */`(item: Item) -> Item { return item; }`
+      .$uses({ Item });
+    const myLayout = tgpu
+      .bindGroupLayout({ foo: { uniform: d.vec3f } })
+      .$idx(0);
+
+    expect(getName(myBuffer)).toBe('myBuffer');
+    expect(getName(myFn)).toBe('myFn');
+    expect(getName(myLayout)).toBe('myLayout');
+  });
+
+  it('does not rename already named resources', () => {
+    const myStruct = d.struct({ a: d.u32 }).$name('IntStruct');
+    const myFunction = tgpu['~unstable'].fn([])(() => 0).$name('ConstFunction');
+
+    expect(getName(myStruct)).toBe('IntStruct');
+    expect(getName(myFunction)).toBe('ConstFunction');
+  });
+
+  it('names TGPU functions', () => {
+    const myFunction = tgpu['~unstable'].fn([])(() => 0);
+    const myComputeFn = tgpu['~unstable'].computeFn({ workgroupSize: [1] })(
+      () => {},
+    );
+    const myVertexFn = tgpu['~unstable'].vertexFn({ out: { ret: d.i32 } })(
+      () => ({ ret: 0 }),
+    );
+    const myFragmentFn = tgpu['~unstable'].fragmentFn({
+      in: { position: d.builtin.position },
+      out: d.vec4f,
+    })(
+      () => d.vec4f(),
+    );
+
+    expect(getName(myFunction)).toBe('myFunction');
+    expect(getName(myComputeFn)).toBe('myComputeFn');
+    expect(getName(myVertexFn)).toBe('myVertexFn');
+    expect(getName(myFragmentFn)).toBe('myFragmentFn');
+  });
+
+  // TODO: make it work
+  // it('names arrow functions', () => {
+  //   const myFun = () => {
+  //     'kernel & js';
+  //     return 0;
+  //   };
+
+  //   const myGpuFun = tgpu['~unstable'].fn([], d.u32)(myFun);
+
+  //   expect(getName(myFun)).toBe('myFun');
+  //   expect(getName(myGpuFun)).toBe('myGpuFun');
+  // });
+
+  // TODO: make it work
+  // it('names function expression', () => {
+  //   const myFun = function () {
+  //     'kernel & js';
+  //     return 0;
+  //   };
+
+  //   const myGpuFun = tgpu['~unstable'].fn([], d.u32)(myFun);
+
+  //   expect(getName(myFun)).toBe('myFun');
+  //   expect(getName(myGpuFun)).toBe('myGpuFun');
+  // });
+
+  // TODO: make it work
+  // it('names function definition', () => {
+  //   function myFun() {
+  //     'kernel & js';
+  //     return 0;
+  //   }
+
+  //   const myGpuFun = tgpu['~unstable'].fn([], d.u32)(myFun);
+
+  //   expect(getName(myFun)).toBe('myFun');
+  //   expect(getName(myGpuFun)).toBe('myGpuFun');
+  // });
+});
diff --git a/packages/unplugin-typegpu/test/aliasing.test.ts b/packages/unplugin-typegpu/test/aliasing.test.ts
index c4f5a9a6..f9dc109f 100644
--- a/packages/unplugin-typegpu/test/aliasing.test.ts
+++ b/packages/unplugin-typegpu/test/aliasing.test.ts
@@ -84,15 +84,13 @@ describe('[ROLLUP] tgpu alias gathering', () => {
       "import hello from 'typegpu';
 
       hello['~unstable']
-              .fn([])(
-                    (($) => ((globalThis.__TYPEGPU_META__ ??= new WeakMap()).set(
-                      $.f = (() => {
+              .fn([])((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => {
                         throw new Error(\`The function "<unnamed>" is invokable only on the GPU. If you want to use it on the CPU, mark it with the "kernel & js" directive.\`);
-                      }) , {
+                      }), {
                     v: 1,
                     ast: {"params":[],"body":[0,[[13,"x",[1,[5,"2"],"+",[5,"2"]]]]],"externalNames":[]},
                     externals: {},
-                  }) && $.f))({}));
+                  }) && $.f)({})));
       "
     `);
   });
@@ -112,15 +110,13 @@ describe('[ROLLUP] tgpu alias gathering', () => {
       "import { tgpu } from 'typegpu';
 
       tgpu['~unstable']
-              .fn([])(
-                    (($) => ((globalThis.__TYPEGPU_META__ ??= new WeakMap()).set(
-                      $.f = (() => {
+              .fn([])((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => {
                         throw new Error(\`The function "<unnamed>" is invokable only on the GPU. If you want to use it on the CPU, mark it with the "kernel & js" directive.\`);
-                      }) , {
+                      }), {
                     v: 1,
                     ast: {"params":[],"body":[0,[[13,"x",[1,[5,"2"],"+",[5,"2"]]]]],"externalNames":[]},
                     externals: {},
-                  }) && $.f))({}));
+                  }) && $.f)({})));
       "
     `);
   });
@@ -139,15 +135,13 @@ describe('[ROLLUP] tgpu alias gathering', () => {
       "import * as t from 'typegpu';
 
       t.tgpu['~unstable']
-              .fn([])(
-                    (($) => ((globalThis.__TYPEGPU_META__ ??= new WeakMap()).set(
-                      $.f = (() => {
+              .fn([])((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => {
                         throw new Error(\`The function "<unnamed>" is invokable only on the GPU. If you want to use it on the CPU, mark it with the "kernel & js" directive.\`);
-                      }) , {
+                      }), {
                     v: 1,
                     ast: {"params":[],"body":[0,[[13,"x",[1,[5,"2"],"+",[5,"2"]]]]],"externalNames":[]},
                     externals: {},
-                  }) && $.f))({}));
+                  }) && $.f)({})));
       "
     `);
   });
diff --git a/packages/unplugin-typegpu/test/auto-naming.test.ts b/packages/unplugin-typegpu/test/auto-naming.test.ts
new file mode 100644
index 00000000..a5dba5f7
--- /dev/null
+++ b/packages/unplugin-typegpu/test/auto-naming.test.ts
@@ -0,0 +1,508 @@
+import { describe, expect, it } from 'vitest';
+import { babelTransform, rollupTransform } from './transform.ts';
+
+describe('[BABEL] auto naming', () => {
+  it('works with tgpu items', () => {
+    const code = `\
+      import tgpu from 'typegpu';
+      import * as d from 'typegpu/data';
+
+      const bindGroupLayout = tgpu.bindGroupLayout({});
+      const vertexLayout = tgpu.vertexLayout((n) => d.arrayOf(d.u32, n));
+
+      console.log(bindGroupLayout, vertexLayout);
+    `;
+
+    expect(babelTransform(code, { autoNamingEnabled: true }))
+      .toMatchInlineSnapshot(`
+      "import tgpu from 'typegpu';
+      import * as d from 'typegpu/data';
+      const bindGroupLayout = (globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.bindGroupLayout({}), "bindGroupLayout");
+      const vertexLayout = (globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.vertexLayout(n => d.arrayOf(d.u32, n)), "vertexLayout");
+      console.log(bindGroupLayout, vertexLayout);"
+    `);
+  });
+
+  it(`works with tgpu['~unstable'] items`, () => {
+    const code = `\
+      import tgpu from 'typegpu';
+      import * as d from 'typegpu/data';
+
+      let nothing, accessor = tgpu['~unstable'].accessor(d.u32);
+      let shell = tgpu['~unstable'].fn([]);
+      var fn = tgpu['~unstable'].fn([])(() => {});
+      const cst = tgpu['~unstable'].const(d.u32, 1);
+
+      console.log(accessor, shell, fn, cst);
+    `;
+
+    expect(babelTransform(code, { autoNamingEnabled: true }))
+      .toMatchInlineSnapshot(`
+        "import tgpu from 'typegpu';
+        import * as d from 'typegpu/data';
+        let nothing,
+          accessor = (globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu['~unstable'].accessor(d.u32), "accessor");
+        let shell = (globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu['~unstable'].fn([]), "shell");
+        var fn = (globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu['~unstable'].fn([])(($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = () => {
+          throw new Error("The function \\"<unnamed>\\" is invokable only on the GPU. If you want to use it on the CPU, mark it with the \\"kernel & js\\" directive.");
+        }, {
+            v: 1,
+            ast: {"params":[],"body":[0,[]],"externalNames":[]},
+            externals: {},
+          }) && $.f)({})), "fn");
+        const cst = (globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu['~unstable'].const(d.u32, 1), "cst");
+        console.log(accessor, shell, fn, cst);"
+      `);
+  });
+
+  it('works with structs', () => {
+    const code = `\
+      import * as d from 'typegpu/data';
+      import { struct } from 'typegpu/data';
+
+      const myStruct1 = d.struct({ a: d.u32 });
+      const myStruct2 = struct({ a: u32 });
+      const bait = d.i32(1);
+
+      console.log(myStruct1, myStruct2, bait);
+    `;
+
+    expect(babelTransform(code, { autoNamingEnabled: true }))
+      .toMatchInlineSnapshot(`
+      "import * as d from 'typegpu/data';
+      import { struct } from 'typegpu/data';
+      const myStruct1 = (globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(d.struct({
+        a: d.u32
+      }), "myStruct1");
+      const myStruct2 = (globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(struct({
+        a: u32
+      }), "myStruct2");
+      const bait = d.i32(1);
+      console.log(myStruct1, myStruct2, bait);"
+    `);
+  });
+
+  it('works with root items', () => {
+    const code = `\
+      import tgpu from 'typegpu';
+
+      const root = await tgpu.init();
+      const myBuffer = root.createBuffer(d.u32, 2);
+
+      console.log(myBuffer);
+    `;
+
+    expect(babelTransform(code, { autoNamingEnabled: true }))
+      .toMatchInlineSnapshot(`
+      "import tgpu from 'typegpu';
+      const root = await tgpu.init();
+      const myBuffer = (globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(root.createBuffer(d.u32, 2), "myBuffer");
+      console.log(myBuffer);"
+    `);
+  });
+
+  it('works with functions', () => {
+    const code = `\
+      import tgpu from 'typegpu';
+      import * as d from 'typegpu/data';
+    
+      const myFunction = tgpu['~unstable'].fn([])(() => 0);
+      const myComputeFn = tgpu['~unstable'].computeFn({ workgroupSize: [1] })(
+        () => {},
+      );
+      const myVertexFn = tgpu['~unstable'].vertexFn({ out: { ret: d.i32 } })(
+        () => ({ ret: 0 }),
+      );
+      const myFragmentFn = tgpu['~unstable'].fragmentFn({
+        in: { position: d.builtin.position },
+        out: d.vec4f,
+      })(
+        () => d.vec4f(),
+      );
+    `;
+
+    expect(babelTransform(code, { autoNamingEnabled: true }))
+      .toMatchInlineSnapshot(`
+      "import tgpu from 'typegpu';
+      import * as d from 'typegpu/data';
+      const myFunction = (globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu['~unstable'].fn([])(($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = () => {
+        throw new Error("The function \\"<unnamed>\\" is invokable only on the GPU. If you want to use it on the CPU, mark it with the \\"kernel & js\\" directive.");
+      }, {
+          v: 1,
+          ast: {"params":[],"body":[0,[[10,[5,"0"]]]],"externalNames":[]},
+          externals: {},
+        }) && $.f)({})), "myFunction");
+      const myComputeFn = (globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu['~unstable'].computeFn({
+        workgroupSize: [1]
+      })(($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = () => {
+        throw new Error("The function \\"<unnamed>\\" is invokable only on the GPU. If you want to use it on the CPU, mark it with the \\"kernel & js\\" directive.");
+      }, {
+          v: 1,
+          ast: {"params":[],"body":[0,[]],"externalNames":[]},
+          externals: {},
+        }) && $.f)({})), "myComputeFn");
+      const myVertexFn = (globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu['~unstable'].vertexFn({
+        out: {
+          ret: d.i32
+        }
+      })(($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = () => {
+        throw new Error("The function \\"<unnamed>\\" is invokable only on the GPU. If you want to use it on the CPU, mark it with the \\"kernel & js\\" directive.");
+      }, {
+          v: 1,
+          ast: {"params":[],"body":[0,[[10,[104,{"ret":[5,"0"]}]]]],"externalNames":[]},
+          externals: {},
+        }) && $.f)({})), "myVertexFn");
+      const myFragmentFn = (globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu['~unstable'].fragmentFn({
+        in: {
+          position: d.builtin.position
+        },
+        out: d.vec4f
+      })(($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = () => {
+        throw new Error("The function \\"<unnamed>\\" is invokable only on the GPU. If you want to use it on the CPU, mark it with the \\"kernel & js\\" directive.");
+      }, {
+          v: 1,
+          ast: {"params":[],"body":[0,[[10,[6,[7,"d","vec4f"],[]]]]],"externalNames":["d"]},
+          externals: {d},
+        }) && $.f)({})), "myFragmentFn");"
+    `);
+  });
+
+  it('works with nested calls', () => {
+    const code = `
+      const myBuffer = root.createBuffer(d.u32)
+        .$usage('storage')
+        .$addFlags(GPUBufferUsage.STORAGE);
+      const Item = d.struct({ a: d.u32 });
+      const myFn = tgpu['~unstable'].fn(
+        [Item],
+        Item,
+      ) /* wgsl */\`(item: Item) -> Item { return item; }\`
+        .$uses({ Item });
+      const myLayout = tgpu
+        .bindGroupLayout({ foo: { uniform: d.vec3f } })
+        .$idx(0);
+    `;
+
+    expect(babelTransform(code, { autoNamingEnabled: true }))
+      .toMatchInlineSnapshot(`
+        "const myBuffer = (globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(root.createBuffer(d.u32).$usage('storage').$addFlags(GPUBufferUsage.STORAGE), "myBuffer");
+        const Item = (globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(d.struct({
+          a: d.u32
+        }), "Item");
+        const myFn = (globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu['~unstable'].fn([Item], Item) /* wgsl */\`(item: Item) -> Item { return item; }\`.$uses({
+          Item
+        }), "myFn");
+        const myLayout = (globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.bindGroupLayout({
+          foo: {
+            uniform: d.vec3f
+          }
+        }).$idx(0), "myLayout");"
+      `);
+  });
+
+  it('does not name already named items', () => {
+    const code = `\
+      import tgpu from 'typegpu';
+      import * as d from 'typegpu/data';
+      import { struct } from 'typegpu/data';
+
+      const root = await tgpu.init();
+      const myBuffer = root.createBuffer(d.u32, 2).$name('int buffer');
+
+      const myStruct = struct({ a: u32 }).$name('myStruct');
+
+      console.log(myBuffer, myStruct)
+
+    `;
+
+    expect(babelTransform(code, { autoNamingEnabled: true }))
+      .toMatchInlineSnapshot(`
+      "import tgpu from 'typegpu';
+      import * as d from 'typegpu/data';
+      import { struct } from 'typegpu/data';
+      const root = await tgpu.init();
+      const myBuffer = root.createBuffer(d.u32, 2).$name('int buffer');
+      const myStruct = struct({
+        a: u32
+      }).$name('myStruct');
+      console.log(myBuffer, myStruct);"
+    `);
+  });
+
+  it(`doesn't name non-tgpu stuff`, () => {
+    const code = `\
+      const a = 1;
+      const b = "root.createBuffer()";
+      const c = () => {};
+
+      console.log(a, b, c);
+    `;
+
+    expect(babelTransform(code, { autoNamingEnabled: true }))
+      .toMatchInlineSnapshot(`
+      "const a = 1;
+      const b = "root.createBuffer()";
+      const c = () => {};
+      console.log(a, b, c);"
+    `);
+  });
+});
+
+describe('[ROLLUP] auto naming', () => {
+  it('works with tgpu items', async () => {
+    const code = `\
+      import tgpu from 'typegpu';
+      import * as d from 'typegpu/data';
+
+      const bindGroupLayout = tgpu.bindGroupLayout({});
+      const vertexLayout = tgpu.vertexLayout((n) => d.arrayOf(d.u32, n));
+
+      console.log(bindGroupLayout, vertexLayout);
+    `;
+
+    expect(await rollupTransform(code, { autoNamingEnabled: true }))
+      .toMatchInlineSnapshot(`
+        "import tgpu from 'typegpu';
+        import * as d from 'typegpu/data';
+
+        const bindGroupLayout = ((globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.bindGroupLayout({}), "bindGroupLayout"));
+              const vertexLayout = ((globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.vertexLayout((n) => d.arrayOf(d.u32, n)), "vertexLayout"));
+
+              console.log(bindGroupLayout, vertexLayout);
+        "
+      `);
+  });
+
+  it(`works with tgpu['~unstable'] items`, async () => {
+    const code = `\
+      import tgpu from 'typegpu';
+      import * as d from 'typegpu/data';
+
+      let nothing, accessor = tgpu['~unstable'].accessor(d.u32);
+      let shell = tgpu['~unstable'].fn([]);
+      var fn = tgpu['~unstable'].fn([])(() => {});
+      const cst = tgpu['~unstable'].const(d.u32, 1);
+
+      console.log(accessor, shell, fn, cst);
+    `;
+
+    expect(await rollupTransform(code, { autoNamingEnabled: true }))
+      .toMatchInlineSnapshot(`
+        "import tgpu from 'typegpu';
+        import * as d from 'typegpu/data';
+
+        let accessor = ((globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu['~unstable'].accessor(d.u32), "accessor"));
+              let shell = ((globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu['~unstable'].fn([]), "shell"));
+              var fn = ((globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu['~unstable'].fn([])((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => {
+                          throw new Error(\`The function "<unnamed>" is invokable only on the GPU. If you want to use it on the CPU, mark it with the "kernel & js" directive.\`);
+                        }), {
+                      v: 1,
+                      ast: {"params":[],"body":[0,[]],"externalNames":[]},
+                      externals: {},
+                    }) && $.f)({}))), "fn"));
+              const cst = ((globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu['~unstable'].const(d.u32, 1), "cst"));
+
+              console.log(accessor, shell, fn, cst);
+        "
+      `);
+  });
+
+  it('works with structs', async () => {
+    const code = `\
+      import * as d from 'typegpu/data';
+      import { struct } from 'typegpu/data';
+
+      const myStruct1 = d.struct({ a: d.u32 });
+      const myStruct2 = struct({ a: u32 });
+      const bait = d.i32(1);
+
+      console.log(myStruct1, myStruct2, bait);
+    `;
+
+    expect(await rollupTransform(code, { autoNamingEnabled: true }))
+      .toMatchInlineSnapshot(`
+        "import * as d from 'typegpu/data';
+        import { struct } from 'typegpu/data';
+
+        const myStruct1 = ((globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(d.struct({ a: d.u32 }), "myStruct1"));
+              const myStruct2 = ((globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(struct({ a: u32 }), "myStruct2"));
+              const bait = d.i32(1);
+
+              console.log(myStruct1, myStruct2, bait);
+        "
+      `);
+  });
+
+  it('works with root items', async () => {
+    const code = `\
+      import tgpu from 'typegpu';
+
+      const root = await tgpu.init();
+      const myBuffer = root.createBuffer(d.u32, 2);
+
+      console.log(myBuffer);
+    `;
+
+    expect(await rollupTransform(code, { autoNamingEnabled: true }))
+      .toMatchInlineSnapshot(`
+        "import tgpu from 'typegpu';
+
+        const root = await tgpu.init();
+              const myBuffer = ((globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(root.createBuffer(d.u32, 2), "myBuffer"));
+
+              console.log(myBuffer);
+        "
+      `);
+  });
+
+  it('works with functions', async () => {
+    const code = `\
+      import tgpu from 'typegpu';
+      import * as d from 'typegpu/data';
+    
+      const myFunction = tgpu['~unstable'].fn([])(() => 0);
+      const myComputeFn = tgpu['~unstable'].computeFn({ workgroupSize: [1] })(
+        () => {},
+      );
+      const myVertexFn = tgpu['~unstable'].vertexFn({ out: { ret: d.i32 } })(
+        () => ({ ret: 0 }),
+      );
+      const myFragmentFn = tgpu['~unstable'].fragmentFn({
+        in: { position: d.builtin.position },
+        out: d.vec4f,
+      })(
+        () => d.vec4f(),
+      );
+    `;
+
+    expect(await rollupTransform(code, { autoNamingEnabled: true }))
+      .toMatchInlineSnapshot(`
+        "import tgpu from 'typegpu';
+        import * as d from 'typegpu/data';
+
+        ((globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu['~unstable'].fn([])((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => {
+                          throw new Error(\`The function "<unnamed>" is invokable only on the GPU. If you want to use it on the CPU, mark it with the "kernel & js" directive.\`);
+                        }), {
+                      v: 1,
+                      ast: {"params":[],"body":[0,[[10,[5,"0"]]]],"externalNames":[]},
+                      externals: {},
+                    }) && $.f)({}))), "myFunction"));
+              ((globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu['~unstable'].computeFn({ workgroupSize: [1] })(
+                (($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => {
+                          throw new Error(\`The function "<unnamed>" is invokable only on the GPU. If you want to use it on the CPU, mark it with the "kernel & js" directive.\`);
+                        }), {
+                      v: 1,
+                      ast: {"params":[],"body":[0,[]],"externalNames":[]},
+                      externals: {},
+                    }) && $.f)({})),
+              ), "myComputeFn"));
+              ((globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu['~unstable'].vertexFn({ out: { ret: d.i32 } })(
+                (($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => {
+                          throw new Error(\`The function "<unnamed>" is invokable only on the GPU. If you want to use it on the CPU, mark it with the "kernel & js" directive.\`);
+                        }), {
+                      v: 1,
+                      ast: {"params":[],"body":[0,[[10,[104,{"ret":[5,"0"]}]]]],"externalNames":[]},
+                      externals: {},
+                    }) && $.f)({})),
+              ), "myVertexFn"));
+              ((globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu['~unstable'].fragmentFn({
+                in: { position: d.builtin.position },
+                out: d.vec4f,
+              })(
+                (($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => {
+                          throw new Error(\`The function "<unnamed>" is invokable only on the GPU. If you want to use it on the CPU, mark it with the "kernel & js" directive.\`);
+                        }), {
+                      v: 1,
+                      ast: {"params":[],"body":[0,[[10,[6,[7,"d","vec4f"],[]]]]],"externalNames":["d"]},
+                      externals: {d},
+                    }) && $.f)({})),
+              ), "myFragmentFn"));
+        "
+      `);
+  });
+
+  it('works with nested calls', async () => {
+    const code = `
+      const myBuffer = root.createBuffer(d.u32)
+        .$usage('storage')
+        .$addFlags(GPUBufferUsage.STORAGE);
+      const Item = d.struct({ a: d.u32 });
+      const myFn = tgpu['~unstable'].fn(
+        [Item],
+        Item,
+      ) /* wgsl */\`(item: Item) -> Item { return item; }\`
+        .$uses({ Item });
+      const myLayout = tgpu
+        .bindGroupLayout({ foo: { uniform: d.vec3f } })
+        .$idx(0);
+    `;
+
+    expect(await rollupTransform(code, { autoNamingEnabled: true }))
+      .toMatchInlineSnapshot(`
+        "((globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(root.createBuffer(d.u32)
+                .$usage('storage')
+                .$addFlags(GPUBufferUsage.STORAGE), "myBuffer"));
+              const Item = ((globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(d.struct({ a: d.u32 }), "Item"));
+              ((globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu['~unstable'].fn(
+                [Item],
+                Item,
+              ) /* wgsl */\`(item: Item) -> Item { return item; }\`
+                .$uses({ Item }), "myFn"));
+              ((globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu
+                .bindGroupLayout({ foo: { uniform: d.vec3f } })
+                .$idx(0), "myLayout"));
+        "
+      `);
+  });
+
+  it('does not name already named items', async () => {
+    const code = `\
+      import tgpu from 'typegpu';
+      import * as d from 'typegpu/data';
+      import { struct } from 'typegpu/data';
+
+      const root = await tgpu.init();
+      const myBuffer = root.createBuffer(d.u32, 2).$name('int buffer');
+
+      const myStruct = struct({ a: u32 }).$name('myStruct');
+
+      console.log(myBuffer, myStruct)
+
+    `;
+
+    expect(await rollupTransform(code, { autoNamingEnabled: true }))
+      .toMatchInlineSnapshot(`
+      "import tgpu from 'typegpu';
+      import * as d from 'typegpu/data';
+      import { struct } from 'typegpu/data';
+
+      const root = await tgpu.init();
+            const myBuffer = root.createBuffer(d.u32, 2).$name('int buffer');
+
+            const myStruct = struct({ a: u32 }).$name('myStruct');
+
+            console.log(myBuffer, myStruct);
+      "
+    `);
+  });
+
+  it(`doesn't name non-tgpu stuff`, async () => {
+    const code = `\
+      const a = 1;
+      const b = "root.createBuffer()";
+      const c = () => {};
+
+      console.log(a, b, c);
+    `;
+
+    expect(await rollupTransform(code, { autoNamingEnabled: true }))
+      .toMatchInlineSnapshot(`
+      "const a = 1;
+            const b = "root.createBuffer()";
+            const c = () => {};
+
+            console.log(a, b, c);
+      "
+    `);
+  });
+});
diff --git a/packages/unplugin-typegpu/test/kernel-and-js-directive.test.ts b/packages/unplugin-typegpu/test/kernel-and-js-directive.test.ts
index 8919ac36..f728e991 100644
--- a/packages/unplugin-typegpu/test/kernel-and-js-directive.test.ts
+++ b/packages/unplugin-typegpu/test/kernel-and-js-directive.test.ts
@@ -260,16 +260,14 @@ describe('[ROLLUP] "kernel & js" directive', () => {
     expect(await rollupTransform(code)).toMatchInlineSnapshot(`
       "import 'typegpu';
 
-      const addGPU = 
-                    (($) => ((globalThis.__TYPEGPU_META__ ??= new WeakMap()).set(
-                      $.f = ((a, b) => {
+      const addGPU = (($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = ((a, b) => {
               'kernel & js';
               return a + b;
-            }) , {
+            }), {
                     v: 1,
                     ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":[]},
                     externals: {},
-                  }) && $.f))({});
+                  }) && $.f)({}));
 
             console.log(addGPU);
 
@@ -303,16 +301,14 @@ describe('[ROLLUP] "kernel & js" directive', () => {
 
       const shell = tgpu['~unstable'].fn([]);
 
-            shell(
-                    (($) => ((globalThis.__TYPEGPU_META__ ??= new WeakMap()).set(
-                      $.f = ((a, b) => {
+            shell((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = ((a, b) => {
               'kernel & js';
               return a + b;
-            }) , {
+            }), {
                     v: 1,
                     ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":[]},
                     externals: {},
-                  }) && $.f))({}));
+                  }) && $.f)({})));
 
             shell((a, b) => {
               return a + b;
@@ -334,16 +330,14 @@ describe('[ROLLUP] "kernel & js" directive', () => {
     expect(await rollupTransform(code)).toMatchInlineSnapshot(`
       "import tgpu from 'typegpu';
 
-      tgpu['~unstable'].fn([])(
-                    (($) => ((globalThis.__TYPEGPU_META__ ??= new WeakMap()).set(
-                      $.f = ((a, b) => {
+      tgpu['~unstable'].fn([])((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = ((a, b) => {
               'kernel & js';
               return a + b;
-            }) , {
+            }), {
                     v: 1,
                     ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":[]},
                     externals: {},
-                  }) && $.f))({}));
+                  }) && $.f)({})));
       "
     `);
   });
@@ -368,16 +362,14 @@ describe('[ROLLUP] "kernel & js" directive', () => {
 
       const shell = tgpu['~unstable'].fn([]);
 
-            shell(
-                    (($) => ((globalThis.__TYPEGPU_META__ ??= new WeakMap()).set(
-                      $.f = (function(a, b){
+            shell((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (function(a, b){
               'kernel & js';
               return a + b;
-            }) , {
+            }), {
                     v: 1,
                     ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":[]},
                     externals: {},
-                  }) && $.f))({}));
+                  }) && $.f)({})));
 
             shell(function(a, b) {
               return a + b;
@@ -407,16 +399,14 @@ describe('[ROLLUP] "kernel & js" directive', () => {
 
       const shell = tgpu['~unstable'].fn([]);
 
-            shell(
-                    (($) => ((globalThis.__TYPEGPU_META__ ??= new WeakMap()).set(
-                      $.f = (function addGPU(a, b){
+            shell((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (function addGPU(a, b){
               'kernel & js';
               return a + b;
-            }) , {
+            }), {
                     v: 1,
                     ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":[]},
                     externals: {},
-                  }) && $.f))({}));
+                  }) && $.f)({})));
 
             shell(function addCPU(a, b) {
               return a + b;
@@ -446,16 +436,14 @@ describe('[ROLLUP] "kernel & js" directive', () => {
     expect(await rollupTransform(code)).toMatchInlineSnapshot(`
       "import 'typegpu';
 
-      const addGPU = 
-                    (($) => ((globalThis.__TYPEGPU_META__ ??= new WeakMap()).set(
-                      $.f = (function addGPU(a, b) {
+      const addGPU = (($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (function addGPU(a, b) {
               'kernel & js';
               return a + b;
-            }) , {
+            }), {
                     v: 1,
                     ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":[]},
                     externals: {},
-                  }) && $.f))({});
+                  }) && $.f)({}));
 
             console.log(addGPU);
 
@@ -494,15 +482,14 @@ describe('[ROLLUP] "kernel & js" directive', () => {
     `;
 
     expect(await rollupTransform(code)).toMatchInlineSnapshot(`
-      "(($) => ((globalThis.__TYPEGPU_META__ ??= new WeakMap()).set(
-                      $.f = (function add(a, b) {
+      "(($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (function add(a, b) {
               'kernel & js';
               return a + b;
-            }) , {
+            }), {
                     v: 1,
                     ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":[]},
                     externals: {},
-                  }) && $.f))({});
+                  }) && $.f)({}));
       "
     `);
   });
diff --git a/packages/unplugin-typegpu/test/kernel-directive.test.ts b/packages/unplugin-typegpu/test/kernel-directive.test.ts
index eb7f33c7..30466673 100644
--- a/packages/unplugin-typegpu/test/kernel-directive.test.ts
+++ b/packages/unplugin-typegpu/test/kernel-directive.test.ts
@@ -179,15 +179,13 @@ describe('[ROLLUP] "kernel" directive', () => {
     expect(await rollupTransform(code)).toMatchInlineSnapshot(`
       "import 'typegpu';
 
-      const addGPU = 
-                    (($) => ((globalThis.__TYPEGPU_META__ ??= new WeakMap()).set(
-                      $.f = (() => {
+      const addGPU = (($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => {
                         throw new Error(\`The function "addGPU" is invokable only on the GPU. If you want to use it on the CPU, mark it with the "kernel & js" directive.\`);
-                      }) , {
+                      }), {
                     v: 1,
                     ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":[]},
                     externals: {},
-                  }) && $.f))({});
+                  }) && $.f)({}));
 
             console.log(addGPU);
 
@@ -221,15 +219,13 @@ describe('[ROLLUP] "kernel" directive', () => {
 
       const shell = tgpu['~unstable'].fn([]);
 
-            shell(
-                    (($) => ((globalThis.__TYPEGPU_META__ ??= new WeakMap()).set(
-                      $.f = (() => {
+            shell((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => {
                         throw new Error(\`The function "<unnamed>" is invokable only on the GPU. If you want to use it on the CPU, mark it with the "kernel & js" directive.\`);
-                      }) , {
+                      }), {
                     v: 1,
                     ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":[]},
                     externals: {},
-                  }) && $.f))({}));
+                  }) && $.f)({})));
 
             shell((a, b) => {
               return a + b;
@@ -258,15 +254,13 @@ describe('[ROLLUP] "kernel" directive', () => {
 
       const shell = tgpu['~unstable'].fn([]);
 
-            shell(
-                    (($) => ((globalThis.__TYPEGPU_META__ ??= new WeakMap()).set(
-                      $.f = (() => {
+            shell((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => {
                         throw new Error(\`The function "<unnamed>" is invokable only on the GPU. If you want to use it on the CPU, mark it with the "kernel & js" directive.\`);
-                      }) , {
+                      }), {
                     v: 1,
                     ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":[]},
                     externals: {},
-                  }) && $.f))({}));
+                  }) && $.f)({})));
 
             shell(function(a, b) {
               return a + b;
@@ -296,15 +290,13 @@ describe('[ROLLUP] "kernel" directive', () => {
 
       const shell = tgpu['~unstable'].fn([]);
 
-            shell(
-                    (($) => ((globalThis.__TYPEGPU_META__ ??= new WeakMap()).set(
-                      $.f = (() => {
+            shell((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => {
                         throw new Error(\`The function "addGPU" is invokable only on the GPU. If you want to use it on the CPU, mark it with the "kernel & js" directive.\`);
-                      }) , {
+                      }), {
                     v: 1,
                     ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":[]},
                     externals: {},
-                  }) && $.f))({}));
+                  }) && $.f)({})));
 
             shell(function addCPU(a, b) {
               return a + b;
@@ -334,15 +326,13 @@ describe('[ROLLUP] "kernel" directive', () => {
     expect(await rollupTransform(code)).toMatchInlineSnapshot(`
       "import 'typegpu';
 
-      const addGPU = 
-                    (($) => ((globalThis.__TYPEGPU_META__ ??= new WeakMap()).set(
-                      $.f = (() => {
+      const addGPU = (($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => {
                         throw new Error(\`The function "addGPU" is invokable only on the GPU. If you want to use it on the CPU, mark it with the "kernel & js" directive.\`);
-                      }) , {
+                      }), {
                     v: 1,
                     ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":[]},
                     externals: {},
-                  }) && $.f))({});
+                  }) && $.f)({}));
 
             console.log(addGPU);
 
diff --git a/packages/unplugin-typegpu/test/parser-options.test.ts b/packages/unplugin-typegpu/test/parser-options.test.ts
index efd64a78..1a15482a 100644
--- a/packages/unplugin-typegpu/test/parser-options.test.ts
+++ b/packages/unplugin-typegpu/test/parser-options.test.ts
@@ -61,15 +61,13 @@ describe('[ROLLUP] tgpu alias gathering', async () => {
       "import tgpu from 'typegpu';
 
       const increment = tgpu['~unstable']
-              .fn([])(
-                    (($) => ((globalThis.__TYPEGPU_META__ ??= new WeakMap()).set(
-                      $.f = (() => {
+              .fn([])((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => {
                         throw new Error(\`The function "<unnamed>" is invokable only on the GPU. If you want to use it on the CPU, mark it with the "kernel & js" directive.\`);
-                      }) , {
+                      }), {
                     v: 1,
                     ast: {"params":[],"body":[0,[[13,"x",[1,[5,"2"],"+",[5,"2"]]]]],"externalNames":[]},
                     externals: {},
-                  }) && $.f))({}));
+                  }) && $.f)({})));
 
             console.log(increment);
       "
diff --git a/packages/unplugin-typegpu/test/tgsl-transpiling-with-does.test.ts b/packages/unplugin-typegpu/test/tgsl-transpiling-with-does.test.ts
index 4eeaa95e..e38e7b74 100644
--- a/packages/unplugin-typegpu/test/tgsl-transpiling-with-does.test.ts
+++ b/packages/unplugin-typegpu/test/tgsl-transpiling-with-does.test.ts
@@ -125,15 +125,13 @@ describe('[ROLLUP] plugin for transpiling tgsl functions to tinyest', () => {
               
               tgpu['~unstable']
                   .computeFn({ in: { num: d.builtin.numWorkgroups }, workgroupSize: [1] })
-                  .does(
-                    (($) => ((globalThis.__TYPEGPU_META__ ??= new WeakMap()).set(
-                      $.f = (() => {
+                  .does((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => {
                         throw new Error(\`The function "<unnamed>" is invokable only on the GPU. If you want to use it on the CPU, mark it with the "kernel & js" directive.\`);
-                      }) , {
+                      }), {
                     v: 1,
                     ast: {"params":[{"type":"i","name":"input"}],"body":[0,[[13,"tmp",[7,[7,"counter","value"],"x"]],[2,[7,[7,"counter","value"],"x"],"=",[7,[7,"counter","value"],"y"]],[2,[7,[7,"counter","value"],"y"],"+=","tmp"],[2,[7,[7,"counter","value"],"z"],"+=",[6,[7,"d","f32"],[[7,[7,"input","num"],"x"]]]]]],"externalNames":["counter","d"]},
                     externals: {counter, d},
-                  }) && $.f))({}));
+                  }) && $.f)({})));
       "
     `);
   });
@@ -159,36 +157,30 @@ describe('[ROLLUP] plugin for transpiling tgsl functions to tinyest', () => {
     expect(await rollupTransform(code)).toMatchInlineSnapshot(`
       "import tgpu from 'typegpu';
 
-      tgpu['~unstable'].computeFn({ workgroupSize: [1] }).does(
-                    (($) => ((globalThis.__TYPEGPU_META__ ??= new WeakMap()).set(
-                      $.f = (() => {
+      tgpu['~unstable'].computeFn({ workgroupSize: [1] }).does((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => {
                         throw new Error(\`The function "<unnamed>" is invokable only on the GPU. If you want to use it on the CPU, mark it with the "kernel & js" directive.\`);
-                      }) , {
+                      }), {
                     v: 1,
                     ast: {"params":[{"type":"i","name":"input"}],"body":[0,[[13,"x",true]]],"externalNames":[]},
                     externals: {},
-                  }) && $.f))({}));
+                  }) && $.f)({})));
 
-              tgpu['~unstable'].fn([]).does(
-                    (($) => ((globalThis.__TYPEGPU_META__ ??= new WeakMap()).set(
-                      $.f = (() => {
+              tgpu['~unstable'].fn([]).does((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => {
                         throw new Error(\`The function "<unnamed>" is invokable only on the GPU. If you want to use it on the CPU, mark it with the "kernel & js" directive.\`);
-                      }) , {
+                      }), {
                     v: 1,
                     ast: {"params":[],"body":[0,[[13,"y",[1,[5,"2"],"+",[5,"2"]]]]],"externalNames":[]},
                     externals: {},
-                  }) && $.f))({}));
+                  }) && $.f)({})));
 
               const cx = 2;
-              tgpu['~unstable'].fn([]).does(
-                    (($) => ((globalThis.__TYPEGPU_META__ ??= new WeakMap()).set(
-                      $.f = (() => {
+              tgpu['~unstable'].fn([]).does((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => {
                         throw new Error(\`The function "<unnamed>" is invokable only on the GPU. If you want to use it on the CPU, mark it with the "kernel & js" directive.\`);
-                      }) , {
+                      }), {
                     v: 1,
                     ast: {"params":[],"body":[0,[[10,"cx"]]],"externalNames":["cx"]},
                     externals: {cx},
-                  }) && $.f))({}));
+                  }) && $.f)({})));
 
               tgpu['~unstable'].fn([]).does('() {}');
       "
diff --git a/packages/unplugin-typegpu/test/tgsl-transpiling.test.ts b/packages/unplugin-typegpu/test/tgsl-transpiling.test.ts
index e5230a9c..d3079f32 100644
--- a/packages/unplugin-typegpu/test/tgsl-transpiling.test.ts
+++ b/packages/unplugin-typegpu/test/tgsl-transpiling.test.ts
@@ -184,15 +184,13 @@ describe('[ROLLUP] plugin for transpiling tgsl functions to tinyest', () => {
               const counter = counterBuffer.as('mutable');
               
               tgpu
-                  .computeFn({ in: { num: d.builtin.numWorkgroups }, workgroupSize: [1] })(
-                    (($) => ((globalThis.__TYPEGPU_META__ ??= new WeakMap()).set(
-                      $.f = (() => {
+                  .computeFn({ in: { num: d.builtin.numWorkgroups }, workgroupSize: [1] })((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => {
                         throw new Error(\`The function "<unnamed>" is invokable only on the GPU. If you want to use it on the CPU, mark it with the "kernel & js" directive.\`);
-                      }) , {
+                      }), {
                     v: 1,
                     ast: {"params":[{"type":"i","name":"input"}],"body":[0,[[13,"tmp",[7,[7,"counter","value"],"x"]],[2,[7,[7,"counter","value"],"x"],"=",[7,[7,"counter","value"],"y"]],[2,[7,[7,"counter","value"],"y"],"+=","tmp"],[2,[7,[7,"counter","value"],"z"],"+=",[6,[7,"d","f32"],[[7,[7,"input","num"],"x"]]]]]],"externalNames":["counter","d"]},
                     externals: {counter, d},
-                  }) && $.f))({}));
+                  }) && $.f)({})));
       "
     `);
   });
@@ -218,36 +216,30 @@ describe('[ROLLUP] plugin for transpiling tgsl functions to tinyest', () => {
     expect(await rollupTransform(code)).toMatchInlineSnapshot(`
       "import tgpu from 'typegpu';
 
-      tgpu['~unstable'].computeFn({ workgroupSize: [1] })(
-                    (($) => ((globalThis.__TYPEGPU_META__ ??= new WeakMap()).set(
-                      $.f = (() => {
+      tgpu['~unstable'].computeFn({ workgroupSize: [1] })((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => {
                         throw new Error(\`The function "<unnamed>" is invokable only on the GPU. If you want to use it on the CPU, mark it with the "kernel & js" directive.\`);
-                      }) , {
+                      }), {
                     v: 1,
                     ast: {"params":[{"type":"i","name":"input"}],"body":[0,[[13,"x",true]]],"externalNames":[]},
                     externals: {},
-                  }) && $.f))({}));
+                  }) && $.f)({})));
 
-              tgpu['~unstable'].fn([])(
-                    (($) => ((globalThis.__TYPEGPU_META__ ??= new WeakMap()).set(
-                      $.f = (() => {
+              tgpu['~unstable'].fn([])((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => {
                         throw new Error(\`The function "<unnamed>" is invokable only on the GPU. If you want to use it on the CPU, mark it with the "kernel & js" directive.\`);
-                      }) , {
+                      }), {
                     v: 1,
                     ast: {"params":[],"body":[0,[[13,"y",[1,[5,"2"],"+",[5,"2"]]]]],"externalNames":[]},
                     externals: {},
-                  }) && $.f))({}));
+                  }) && $.f)({})));
 
               const cx = 2;
-              tgpu['~unstable'].fn([])(
-                    (($) => ((globalThis.__TYPEGPU_META__ ??= new WeakMap()).set(
-                      $.f = (() => {
+              tgpu['~unstable'].fn([])((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => {
                         throw new Error(\`The function "<unnamed>" is invokable only on the GPU. If you want to use it on the CPU, mark it with the "kernel & js" directive.\`);
-                      }) , {
+                      }), {
                     v: 1,
                     ast: {"params":[],"body":[0,[[10,"cx"]]],"externalNames":["cx"]},
                     externals: {cx},
-                  }) && $.f))({}));
+                  }) && $.f)({})));
 
               tgpu['~unstable'].fn([])('() {}');
       "
diff --git a/packages/unplugin-typegpu/test/transform.ts b/packages/unplugin-typegpu/test/transform.ts
index fccfc8f8..3c3341b4 100644
--- a/packages/unplugin-typegpu/test/transform.ts
+++ b/packages/unplugin-typegpu/test/transform.ts
@@ -7,18 +7,22 @@ import rollupPlugin from '../src/rollup.ts';
 
 const defaultOptions: Options = {
   include: [/\.m?[jt]sx?$/, /virtual:/],
+  autoNamingEnabled: false,
 };
 
-export const babelTransform = (code: string, options = defaultOptions) =>
+export const babelTransform = (code: string, options?: Options) =>
   Babel.transform(code, {
-    plugins: [[babelPlugin, options]],
+    plugins: [[babelPlugin, { ...defaultOptions, ...options }]],
     parserOpts: { plugins: ['typescript'] },
   }).code;
 
-export const rollupTransform = (code: string, options = defaultOptions) =>
+export const rollupTransform = (code: string, options?: Options) =>
   rollup({
     input: 'code',
-    plugins: [virtual({ code }), rollupPlugin(options)],
+    plugins: [
+      virtual({ code }),
+      rollupPlugin({ ...defaultOptions, ...options }),
+    ],
     external: ['typegpu', /^typegpu\/.*$/],
   })
     .then((build) => build.generate({}))