From 23501004465ca37e2f2310e4027c9826f8cbb656 Mon Sep 17 00:00:00 2001 From: Aleksander Katan <56294622+aleksanderkatan@users.noreply.github.com> Date: Tue, 28 Apr 2026 15:34:57 +0200 Subject: [PATCH 01/12] Add tests stub --- .../test/nested-externals.test.ts | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 packages/unplugin-typegpu/test/nested-externals.test.ts diff --git a/packages/unplugin-typegpu/test/nested-externals.test.ts b/packages/unplugin-typegpu/test/nested-externals.test.ts new file mode 100644 index 0000000000..c21791cab3 --- /dev/null +++ b/packages/unplugin-typegpu/test/nested-externals.test.ts @@ -0,0 +1,67 @@ +import { describe, expect, it } from 'vitest'; +import { babelTransform, rollupTransform } from './transform.ts'; + +function extractExternals(code: string | undefined | null) { + if (!code) { + throw new Error('Expected code to be truthy.'); + } + const startIndex = code.indexOf('externals:') + 'externals:'.length; + const endIndex = code.indexOf('}) && $.f'); + return code.slice(startIndex, endIndex).trim(); +} + +describe('externals gathering', () => { + describe('BABEL', () => { + it('allows multiple usages of one external', () => { + const code = `\ + const ext = { + value: 7, + config: { + multiplier: 1, + zero: 0, + } + }; + const foo = () => { + 'use gpu'; + const a = ext.value; + const b = ext.config.multiplier; + const c = ext.config.zero; + const d = ext.config.multiplier; + }; + console.log(foo);`; + + expect(extractExternals(babelTransform(code))).toMatchInlineSnapshot(` + "() => { + return { + ext + }; + }" + `); + }); + }); + + describe('ROLLUP', () => { + it('allows multiple usages of one external', async () => { + const code = `\ + const ext = { + value: 7, + config: { + multiplier: 1, + zero: 0, + } + }; + const foo = () => { + 'use gpu'; + const a = ext.value; + const b = ext.config.multiplier; + const c = ext.config.zero; + const d = ext.config.multiplier; + }; + console.log(foo);`; + + expect(extractExternals(await rollupTransform(code))).toMatchInlineSnapshot( + `"() => ({ext}),"`, + ); + }); + }); +}); From b9ef0c6f4be6943d2fdeb8795b600cdb895ea100 Mon Sep 17 00:00:00 2001 From: Aleksander Katan <56294622+aleksanderkatan@users.noreply.github.com> Date: Tue, 28 Apr 2026 15:47:50 +0200 Subject: [PATCH 02/12] Add more babel tests --- .../test/nested-externals.test.ts | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/packages/unplugin-typegpu/test/nested-externals.test.ts b/packages/unplugin-typegpu/test/nested-externals.test.ts index c21791cab3..ffcca02339 100644 --- a/packages/unplugin-typegpu/test/nested-externals.test.ts +++ b/packages/unplugin-typegpu/test/nested-externals.test.ts @@ -12,6 +12,7 @@ function extractExternals(code: string | undefined | null) { describe('externals gathering', () => { describe('BABEL', () => { + // TODO: fix all this code it('allows multiple usages of one external', () => { const code = `\ const ext = { @@ -38,6 +39,59 @@ describe('externals gathering', () => { }" `); }); + + it('treats dereference like a regular external', () => { + const code = `\ + const buffer = root.createMutable(d.vec2u); + const foo = () => { + 'use gpu'; + const d = buffer.$.x; + }; + console.log(foo);`; + + expect(extractExternals(babelTransform(code))).toMatchInlineSnapshot(` + "() => { + return { + buffer + }; + }" + `); + }); + + it('skips computed prop access', () => { + const code = `\ + const fn = () => { + 'use gpu'; + const x = buffers['buf'].$.pos.x; + }; + console.log(fn); + `; + + expect(extractExternals(babelTransform(code))).toMatchInlineSnapshot(` + "() => { + return { + buffer + }; + }" + `); + }); + + it('skips calls', () => { + const code = `\ + const fn5 = () => { + 'use gpu'; + const x = a.b.comptime().c; + } + `; + + expect(extractExternals(babelTransform(code))).toMatchInlineSnapshot(` + "() => { + return { + buffer + }; + }" + `); + }); }); describe('ROLLUP', () => { From 1729ca54b0b4309fffc32872d71ae6d01d51ca1a Mon Sep 17 00:00:00 2001 From: Aleksander Katan <56294622+aleksanderkatan@users.noreply.github.com> Date: Tue, 28 Apr 2026 15:52:19 +0200 Subject: [PATCH 03/12] Update babel tests --- .../test/nested-externals.test.ts | 66 +++++++++---------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/packages/unplugin-typegpu/test/nested-externals.test.ts b/packages/unplugin-typegpu/test/nested-externals.test.ts index ffcca02339..028717e757 100644 --- a/packages/unplugin-typegpu/test/nested-externals.test.ts +++ b/packages/unplugin-typegpu/test/nested-externals.test.ts @@ -12,7 +12,6 @@ function extractExternals(code: string | undefined | null) { describe('externals gathering', () => { describe('BABEL', () => { - // TODO: fix all this code it('allows multiple usages of one external', () => { const code = `\ const ext = { @@ -42,10 +41,13 @@ describe('externals gathering', () => { it('treats dereference like a regular external', () => { const code = `\ + import tgpu, { d } from 'typegpu'; + + const root = await tgpu.init(); const buffer = root.createMutable(d.vec2u); const foo = () => { 'use gpu'; - const d = buffer.$.x; + const a = buffer.$.x; }; console.log(foo);`; @@ -60,17 +62,20 @@ describe('externals gathering', () => { it('skips computed prop access', () => { const code = `\ + const ext = { + n: 1, + }; + const fn = () => { 'use gpu'; - const x = buffers['buf'].$.pos.x; + const a = ext['n']; }; - console.log(fn); - `; + console.log(fn);`; expect(extractExternals(babelTransform(code))).toMatchInlineSnapshot(` "() => { return { - buffer + ext }; }" `); @@ -78,44 +83,39 @@ describe('externals gathering', () => { it('skips calls', () => { const code = `\ - const fn5 = () => { + import tgpu, { d } from 'typegpu'; + + const ext = { + comptime: tgpu.comptime(() => { + return d.vec4f(); + }), + runtime: () => { + 'use gpu'; + return d.vec4f(); + }, + }; + + const fn = () => { 'use gpu'; - const x = a.b.comptime().c; - } - `; + const a = ext.comptime().x; + const b = ext.runtime().y; + };`; expect(extractExternals(babelTransform(code))).toMatchInlineSnapshot(` "() => { - return { - buffer - }; - }" + return { + d + }; + }" `); }); }); describe('ROLLUP', () => { it('allows multiple usages of one external', async () => { - const code = `\ - const ext = { - value: 7, - config: { - multiplier: 1, - zero: 0, - } - }; - const foo = () => { - 'use gpu'; - const a = ext.value; - const b = ext.config.multiplier; - const c = ext.config.zero; - const d = ext.config.multiplier; - }; - console.log(foo);`; + const code = ``; - expect(extractExternals(await rollupTransform(code))).toMatchInlineSnapshot( - `"() => ({ext}),"`, - ); + expect(extractExternals(await rollupTransform(code))).toMatchInlineSnapshot(`""`); }); }); }); From 87d6bc39a8bde8929b3ea05b5f9656505f5eecfa Mon Sep 17 00:00:00 2001 From: Aleksander Katan <56294622+aleksanderkatan@users.noreply.github.com> Date: Tue, 28 Apr 2026 16:02:16 +0200 Subject: [PATCH 04/12] Add rollup tests --- .../test/nested-externals.test.ts | 147 +++++++++++------- 1 file changed, 91 insertions(+), 56 deletions(-) diff --git a/packages/unplugin-typegpu/test/nested-externals.test.ts b/packages/unplugin-typegpu/test/nested-externals.test.ts index 028717e757..55dc6ddc17 100644 --- a/packages/unplugin-typegpu/test/nested-externals.test.ts +++ b/packages/unplugin-typegpu/test/nested-externals.test.ts @@ -10,25 +10,71 @@ function extractExternals(code: string | undefined | null) { return code.slice(startIndex, endIndex).trim(); } +const codes = { + 'allows multiple usages of one external': `\ + const ext = { + value: 7, + config: { + multiplier: 1, + zero: 0, + } + }; + const foo = () => { + 'use gpu'; + const a = ext.value; + const b = ext.config.multiplier; + const c = ext.config.zero; + const d = ext.config.multiplier; + }; + console.log(foo);`, + // --- + 'treats dereference like a regular external': `\ + import tgpu, { d } from 'typegpu'; + + const root = await tgpu.init(); + const buffer = root.createMutable(d.vec2u); + const foo = () => { + 'use gpu'; + const a = buffer.$.x; + }; + console.log(foo);`, + // --- + 'skips computed prop access': `\ + const ext = { + n: 1, + }; + + const fn = () => { + 'use gpu'; + const a = ext['n']; + }; + console.log(fn);`, + // --- + 'skips calls': `\ + import tgpu, { d } from 'typegpu'; + + const ext = { + comptime: tgpu.comptime(() => { + return d.vec4f(); + }), + runtime: () => { + 'use gpu'; + return d.vec4f(); + }, + }; + + const fn = () => { + 'use gpu'; + const a = ext.comptime().x; + const b = ext.runtime().y; + }; + console.log(fn);`, +}; + describe('externals gathering', () => { describe('BABEL', () => { it('allows multiple usages of one external', () => { - const code = `\ - const ext = { - value: 7, - config: { - multiplier: 1, - zero: 0, - } - }; - const foo = () => { - 'use gpu'; - const a = ext.value; - const b = ext.config.multiplier; - const c = ext.config.zero; - const d = ext.config.multiplier; - }; - console.log(foo);`; + const code = codes['allows multiple usages of one external']; expect(extractExternals(babelTransform(code))).toMatchInlineSnapshot(` "() => { @@ -40,16 +86,7 @@ describe('externals gathering', () => { }); it('treats dereference like a regular external', () => { - const code = `\ - import tgpu, { d } from 'typegpu'; - - const root = await tgpu.init(); - const buffer = root.createMutable(d.vec2u); - const foo = () => { - 'use gpu'; - const a = buffer.$.x; - }; - console.log(foo);`; + const code = codes['treats dereference like a regular external']; expect(extractExternals(babelTransform(code))).toMatchInlineSnapshot(` "() => { @@ -61,16 +98,7 @@ describe('externals gathering', () => { }); it('skips computed prop access', () => { - const code = `\ - const ext = { - n: 1, - }; - - const fn = () => { - 'use gpu'; - const a = ext['n']; - }; - console.log(fn);`; + const code = codes['skips computed prop access']; expect(extractExternals(babelTransform(code))).toMatchInlineSnapshot(` "() => { @@ -82,24 +110,7 @@ describe('externals gathering', () => { }); it('skips calls', () => { - const code = `\ - import tgpu, { d } from 'typegpu'; - - const ext = { - comptime: tgpu.comptime(() => { - return d.vec4f(); - }), - runtime: () => { - 'use gpu'; - return d.vec4f(); - }, - }; - - const fn = () => { - 'use gpu'; - const a = ext.comptime().x; - const b = ext.runtime().y; - };`; + const code = codes['skips calls']; expect(extractExternals(babelTransform(code))).toMatchInlineSnapshot(` "() => { @@ -113,9 +124,33 @@ describe('externals gathering', () => { describe('ROLLUP', () => { it('allows multiple usages of one external', async () => { - const code = ``; + const code = codes['allows multiple usages of one external']; + + expect(extractExternals(await rollupTransform(code))).toMatchInlineSnapshot( + `"() => ({ext}),"`, + ); + }); + + it('treats dereference like a regular external', async () => { + const code = codes['treats dereference like a regular external']; + + expect(extractExternals(await rollupTransform(code))).toMatchInlineSnapshot( + `"() => ({buffer}),"`, + ); + }); + + it('skips computed prop access', async () => { + const code = codes['skips computed prop access']; + + expect(extractExternals(await rollupTransform(code))).toMatchInlineSnapshot( + `"() => ({ext}),"`, + ); + }); + + it('skips calls', async () => { + const code = codes['skips calls']; - expect(extractExternals(await rollupTransform(code))).toMatchInlineSnapshot(`""`); + expect(extractExternals(await rollupTransform(code))).toMatchInlineSnapshot(`"() => ({d}),"`); }); }); }); From 87dc34b1d0de59983c01d65524e8ff788dd26140 Mon Sep 17 00:00:00 2001 From: Aleksander Katan <56294622+aleksanderkatan@users.noreply.github.com> Date: Tue, 28 Apr 2026 16:58:06 +0200 Subject: [PATCH 05/12] Keep ancestor chain in context in tinyest-for-wgsl --- packages/tinyest-for-wgsl/src/parsers.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/tinyest-for-wgsl/src/parsers.ts b/packages/tinyest-for-wgsl/src/parsers.ts index 17cdc5a777..2bbb72fbee 100644 --- a/packages/tinyest-for-wgsl/src/parsers.ts +++ b/packages/tinyest-for-wgsl/src/parsers.ts @@ -16,6 +16,7 @@ type Context = { /** Used to signal to identifiers that they should not treat their resolution as possible external uses. */ ignoreExternalDepth: number; stack: Scope[]; + ancestorChain: JsNode[]; }; type JsNode = babel.Node | acorn.AnyNode; @@ -308,8 +309,11 @@ function transpile(ctx: Context, node: JsNode): tinyest.AnyNode { throw new Error(`Unsupported JS functionality: ${node.type}`); } + ctx.ancestorChain.push(node); // @ts-expect-error - return transpiler(ctx, node); + const result = transpiler(ctx, node); + ctx.ancestorChain.pop(); + return result; } export type TranspilationResult = { @@ -435,6 +439,7 @@ export function transpileFn(rootNode: JsNode): TranspilationResult { ), }, ], + ancestorChain: [], }; const tinyestBody = transpile(ctx, body); @@ -464,6 +469,7 @@ export function transpileNode(node: JsNode): tinyest.AnyNode { declaredNames: [], }, ], + ancestorChain: [], }; return transpile(ctx, node); From 916b52e10e290131f2a4fc89f6ea6fceda3ea638 Mon Sep 17 00:00:00 2001 From: Aleksander Katan <56294622+aleksanderkatan@users.noreply.github.com> Date: Wed, 29 Apr 2026 12:29:44 +0200 Subject: [PATCH 06/12] Change externals from array to object --- packages/tinyest-for-wgsl/src/parsers.ts | 70 ++++++++++++++++--- .../tinyest-for-wgsl/tests/parsers.test.ts | 57 ++++++++++++--- packages/typegpu/src/core/function/fnCore.ts | 4 +- packages/typegpu/src/core/function/fnTypes.ts | 13 ---- packages/typegpu/src/shared/meta.ts | 6 +- packages/unplugin-typegpu/src/babel.ts | 2 +- packages/unplugin-typegpu/src/core/factory.ts | 2 +- 7 files changed, 120 insertions(+), 34 deletions(-) diff --git a/packages/tinyest-for-wgsl/src/parsers.ts b/packages/tinyest-for-wgsl/src/parsers.ts index 2bbb72fbee..be7eebf379 100644 --- a/packages/tinyest-for-wgsl/src/parsers.ts +++ b/packages/tinyest-for-wgsl/src/parsers.ts @@ -10,9 +10,13 @@ type Scope = { declaredNames: string[]; }; +interface Externals { + [key: string]: Externals | string; +} + type Context = { /** Holds a set of all identifiers that were used in code, but were not declared in code. */ - externalNames: Set; + externalNames: Externals; /** Used to signal to identifiers that they should not treat their resolution as possible external uses. */ ignoreExternalDepth: number; stack: Scope[]; @@ -71,14 +75,63 @@ const Transpilers: Partial<{ Identifier(ctx, node) { if (ctx.ignoreExternalDepth === 0 && !isDeclared(ctx, node.name)) { - ctx.externalNames.add(node.name); + ctx.externalNames[node.name] = node.name; + return node.name; + + // const chain: string[] = []; + + // for (let i = ctx.ancestorChain.length - 1; i >= 0; i--) { + // const current = ctx.ancestorChain[i]; + + // if (!current) { + // break; + // } + + // if (current.type === 'Identifier') { + // chain.push(current.name); + // } else if (current.type === 'MemberExpression' && !current.computed) { + // chain.push(`${(current.property as { name: string }).name}`); + // } else { + // break; + // } + // } + + // let currentExternals = ctx.externalNames; + // for (const elem of chain) { + // let nextExternals = currentExternals.get(elem); + // if (nextExternals) { + // if (typeof nextExternals !== 'string') { + // currentExternals = nextExternals; + // } else { + // // we already need this in externals, so we break + // break; + // } + // } else { + // nextExternals = new Map(); + // currentExternals.set(elem, nextExternals); + // currentExternals = nextExternals; + // } + // } + + // const lastKey = chain[chain.length - 1]; + // if (lastKey !== undefined) { + // let parent = ctx.externalNames; + // for (const key of chain.slice(0, -1)) { + // const next = parent.get(key); + // if (!next || typeof next === 'string') break; + // parent = next; + // } + // if (parent.get(lastKey) instanceof Map) { + // parent.set(lastKey, chain.join('.')); + // } + // } } return node.name; }, ThisExpression(ctx) { - ctx.externalNames.add('this'); + ctx.externalNames['this'] = 'this'; return 'this'; }, @@ -323,7 +376,7 @@ export type TranspilationResult = { * All identifiers found in the function code that are not declared in the * function itself, or in the block that is accessing that identifier. */ - externalNames: string[]; + externalNames: Externals; }; export function extractFunctionParts(rootNode: JsNode): { @@ -428,7 +481,7 @@ export function transpileFn(rootNode: JsNode): TranspilationResult { const { params, body } = extractFunctionParts(rootNode); const ctx: Context = { - externalNames: new Set(), + externalNames: Object.create(null), ignoreExternalDepth: 0, stack: [ { @@ -443,26 +496,25 @@ export function transpileFn(rootNode: JsNode): TranspilationResult { }; const tinyestBody = transpile(ctx, body); - const externalNames = [...ctx.externalNames]; if (body.type === 'BlockStatement') { return { params, body: tinyestBody as tinyest.Block, - externalNames, + externalNames: ctx.externalNames, }; } return { params, body: [NODE.block, [[NODE.return, tinyestBody as tinyest.Expression]]], - externalNames, + externalNames: ctx.externalNames, }; } export function transpileNode(node: JsNode): tinyest.AnyNode { const ctx: Context = { - externalNames: new Set(), + externalNames: Object.create(null), ignoreExternalDepth: 0, stack: [ { diff --git a/packages/tinyest-for-wgsl/tests/parsers.test.ts b/packages/tinyest-for-wgsl/tests/parsers.test.ts index 7b5edb96d2..16b660653c 100644 --- a/packages/tinyest-for-wgsl/tests/parsers.test.ts +++ b/packages/tinyest-for-wgsl/tests/parsers.test.ts @@ -30,7 +30,7 @@ describe('transpileFn', () => { expect(params).toStrictEqual([]); expect(JSON.stringify(body)).toMatchInlineSnapshot(`"[0,[]]"`); - expect(externalNames).toStrictEqual([]); + expect(externalNames).toMatchInlineSnapshot(`{}`); }), ); @@ -41,7 +41,7 @@ describe('transpileFn', () => { expect(params).toStrictEqual([]); expect(JSON.stringify(body)).toMatchInlineSnapshot(`"[0,[]]"`); - expect(externalNames).toStrictEqual([]); + expect(externalNames).toMatchInlineSnapshot(`{}`); }), ); @@ -57,7 +57,11 @@ describe('transpileFn', () => { expect(JSON.stringify(body)).toMatchInlineSnapshot( `"[0,[[10,[1,[1,"a","+","b"],"-","c"]]]]"`, ); - expect(externalNames).toStrictEqual(['c']); + expect(externalNames).toMatchInlineSnapshot(` + { + "c": "c", + } + `); }), ); @@ -76,7 +80,11 @@ describe('transpileFn', () => { `"[0,[[13,"a",[5,"0"]],[2,"c","=",[1,"a","+",[5,"2"]]]]]"`, ); // Only 'c' is external, as 'a' is declared in the same scope. - expect(externalNames).toStrictEqual(['c']); + expect(externalNames).toMatchInlineSnapshot(` + { + "c": "c", + } + `); }), ); @@ -97,7 +105,11 @@ describe('transpileFn', () => { `"[0,[[13,"a",[5,"0"]],[0,[[2,"c","=",[1,"a","+",[5,"2"]]]]]]]"`, ); // Only 'c' is external, as 'a' is declared in the outer scope. - expect(externalNames).toStrictEqual(['c']); + expect(externalNames).toMatchInlineSnapshot(` + { + "c": "c", + } + `); }), ); @@ -111,7 +123,11 @@ describe('transpileFn', () => { `"[0,[[10,[7,[7,"external","outside"],"prop"]]]]"`, ); // Only 'external' is external. - expect(externalNames).toStrictEqual(['external']); + expect(externalNames).toMatchInlineSnapshot(` + { + "external": "external", + } + `); }), ); @@ -140,7 +156,7 @@ describe('transpileFn', () => { }, ]); - expect(externalNames).toStrictEqual([]); + expect(externalNames).toMatchInlineSnapshot(`{}`); }), ); @@ -186,7 +202,7 @@ describe('transpileFn', () => { }, ]); - expect(externalNames).toStrictEqual([]); + expect(externalNames).toMatchInlineSnapshot(`{}`); }), ); @@ -195,4 +211,29 @@ describe('transpileFn', () => { expect(JSON.stringify(body)).toMatchInlineSnapshot(`"[0,[[10,[7,"x","y"]]]]"`); }); + + it( + 'handles complex external trees', + dualTest((p) => { + const { externalNames } = transpileFn( + p(`() => { + const a = ext.p; + const b = ext.q.a; + const c = ext.q.b; + + const d = ext.r.a; + const e = ext.r; + + const f = ext.s; + const g = ext.s.a; + }`), + ); + + expect(externalNames).toMatchInlineSnapshot(` + { + "ext": "ext", + } + `); + }), + ); }); diff --git a/packages/typegpu/src/core/function/fnCore.ts b/packages/typegpu/src/core/function/fnCore.ts index 66429067e5..3d9cd41f81 100644 --- a/packages/typegpu/src/core/function/fnCore.ts +++ b/packages/typegpu/src/core/function/fnCore.ts @@ -170,7 +170,9 @@ export function createFnCore(implementation: Implementation, fnAttribute = ''): } // verify all required externals are present - const missingExternals = ast.externalNames.filter((name) => !(name in externalMap)); + const missingExternals = Object.keys(ast.externalNames).filter( + (name) => !(name in externalMap), + ); if (missingExternals.length > 0) { throw new MissingLinksError(getName(this), missingExternals); } diff --git a/packages/typegpu/src/core/function/fnTypes.ts b/packages/typegpu/src/core/function/fnTypes.ts index 18c1f3213a..07d4935f73 100644 --- a/packages/typegpu/src/core/function/fnTypes.ts +++ b/packages/typegpu/src/core/function/fnTypes.ts @@ -27,19 +27,6 @@ import type { InferGPU } from '../../shared/repr.ts'; export type AnyFn = (...args: never[]) => unknown; -/** - * Information extracted from transpiling a JS function. - */ -export type TranspilationResult = { - params: tinyest.FuncParameter[]; - body: tinyest.Block; - /** - * All identifiers found in the function code that are not declared in the - * function itself, or in the block that is accessing that identifier. - */ - externalNames: string[]; -}; - export type InferArgs = { [Idx in keyof T]: InferGPU; }; diff --git a/packages/typegpu/src/shared/meta.ts b/packages/typegpu/src/shared/meta.ts index d401872629..031c95895a 100644 --- a/packages/typegpu/src/shared/meta.ts +++ b/packages/typegpu/src/shared/meta.ts @@ -4,6 +4,10 @@ import type { Block, FuncParameter } from 'tinyest'; import { DEV, TEST } from './env.ts'; import { $getNameForward, isMarkedInternal } from './symbols.ts'; +export interface Externals { + [key: string]: Externals | string; +} + export interface MetaData { v?: number; name?: string | undefined; @@ -11,7 +15,7 @@ export interface MetaData { | { params: FuncParameter[]; body: Block; - externalNames: string[]; + externalNames: Externals; } | undefined; externals?: diff --git a/packages/unplugin-typegpu/src/babel.ts b/packages/unplugin-typegpu/src/babel.ts index 9e8b904bae..2fd7d0c21b 100644 --- a/packages/unplugin-typegpu/src/babel.ts +++ b/packages/unplugin-typegpu/src/babel.ts @@ -33,7 +33,7 @@ function assignMetadata( t.blockStatement([ t.returnStatement( t.objectExpression( - ast.externalNames.map((name) => + Object.keys(ast.externalNames).map((name) => name === 'this' ? t.objectProperty(i('this'), t.thisExpression()) : t.objectProperty(i(name), i(name), false, /* shorthand */ name !== 'this'), diff --git a/packages/unplugin-typegpu/src/core/factory.ts b/packages/unplugin-typegpu/src/core/factory.ts index 57e6ca66ba..22559354a2 100644 --- a/packages/unplugin-typegpu/src/core/factory.ts +++ b/packages/unplugin-typegpu/src/core/factory.ts @@ -42,7 +42,7 @@ function assignMetadata( v: ${FORMAT_VERSION}, name: ${name ? `"${name}"` : 'undefined'}, ast: ${embedJSON(ast)}, - externals: () => ({${ast.externalNames + externals: () => ({${Object.keys(ast.externalNames) .map((e) => (e === 'this' ? '"this": this' : e)) .join(', ')}}), }`; From 2f44d23d3e53ef89488da10d292cf60a0a5a4b43 Mon Sep 17 00:00:00 2001 From: Aleksander Katan <56294622+aleksanderkatan@users.noreply.github.com> Date: Wed, 29 Apr 2026 12:37:40 +0200 Subject: [PATCH 07/12] Add addExternal function --- packages/tinyest-for-wgsl/src/parsers.ts | 112 +++++++++--------- packages/typegpu/src/core/function/fnTypes.ts | 1 - 2 files changed, 59 insertions(+), 54 deletions(-) diff --git a/packages/tinyest-for-wgsl/src/parsers.ts b/packages/tinyest-for-wgsl/src/parsers.ts index be7eebf379..b9697c53ad 100644 --- a/packages/tinyest-for-wgsl/src/parsers.ts +++ b/packages/tinyest-for-wgsl/src/parsers.ts @@ -33,6 +33,62 @@ const tsFallthrough = (ctx: Context, node: { expression: babel.Expression }): ti return transpile(ctx, node.expression); }; +function addExternal( + ctx: Context, + node: babel.ThisExpression | acorn.ThisExpression | babel.Identifier | acorn.Identifier, +) { + const name = node.type === 'Identifier' ? node.name : 'this'; + ctx.externalNames[name] = name; + + // const chain: string[] = []; + + // for (let i = ctx.ancestorChain.length - 1; i >= 0; i--) { + // const current = ctx.ancestorChain[i]; + + // if (!current) { + // break; + // } + + // if (current.type === 'Identifier') { + // chain.push(current.name); + // } else if (current.type === 'MemberExpression' && !current.computed) { + // chain.push(`${(current.property as { name: string }).name}`); + // } else { + // break; + // } + // } + + // let currentExternals = ctx.externalNames; + // for (const elem of chain) { + // let nextExternals = currentExternals.get(elem); + // if (nextExternals) { + // if (typeof nextExternals !== 'string') { + // currentExternals = nextExternals; + // } else { + // // we already need this in externals, so we break + // break; + // } + // } else { + // nextExternals = new Map(); + // currentExternals.set(elem, nextExternals); + // currentExternals = nextExternals; + // } + // } + + // const lastKey = chain[chain.length - 1]; + // if (lastKey !== undefined) { + // let parent = ctx.externalNames; + // for (const key of chain.slice(0, -1)) { + // const next = parent.get(key); + // if (!next || typeof next === 'string') break; + // parent = next; + // } + // if (parent.get(lastKey) instanceof Map) { + // parent.set(lastKey, chain.join('.')); + // } + // } +} + const Transpilers: Partial<{ [Type in JsNode['type']]: ( ctx: Context, @@ -75,63 +131,13 @@ const Transpilers: Partial<{ Identifier(ctx, node) { if (ctx.ignoreExternalDepth === 0 && !isDeclared(ctx, node.name)) { - ctx.externalNames[node.name] = node.name; - return node.name; - - // const chain: string[] = []; - - // for (let i = ctx.ancestorChain.length - 1; i >= 0; i--) { - // const current = ctx.ancestorChain[i]; - - // if (!current) { - // break; - // } - - // if (current.type === 'Identifier') { - // chain.push(current.name); - // } else if (current.type === 'MemberExpression' && !current.computed) { - // chain.push(`${(current.property as { name: string }).name}`); - // } else { - // break; - // } - // } - - // let currentExternals = ctx.externalNames; - // for (const elem of chain) { - // let nextExternals = currentExternals.get(elem); - // if (nextExternals) { - // if (typeof nextExternals !== 'string') { - // currentExternals = nextExternals; - // } else { - // // we already need this in externals, so we break - // break; - // } - // } else { - // nextExternals = new Map(); - // currentExternals.set(elem, nextExternals); - // currentExternals = nextExternals; - // } - // } - - // const lastKey = chain[chain.length - 1]; - // if (lastKey !== undefined) { - // let parent = ctx.externalNames; - // for (const key of chain.slice(0, -1)) { - // const next = parent.get(key); - // if (!next || typeof next === 'string') break; - // parent = next; - // } - // if (parent.get(lastKey) instanceof Map) { - // parent.set(lastKey, chain.join('.')); - // } - // } + addExternal(ctx, node); } - return node.name; }, - ThisExpression(ctx) { - ctx.externalNames['this'] = 'this'; + ThisExpression(ctx, node) { + addExternal(ctx, node); return 'this'; }, diff --git a/packages/typegpu/src/core/function/fnTypes.ts b/packages/typegpu/src/core/function/fnTypes.ts index 07d4935f73..6d8d762ccf 100644 --- a/packages/typegpu/src/core/function/fnTypes.ts +++ b/packages/typegpu/src/core/function/fnTypes.ts @@ -1,4 +1,3 @@ -import type * as tinyest from 'tinyest'; import type { BuiltinClipDistances } from '../../builtin.ts'; import type { AnyAttribute } from '../../data/attributes.ts'; import type { From e811b07658651dd11e90bcab6399b29b108b1e72 Mon Sep 17 00:00:00 2001 From: Aleksander Katan <56294622+aleksanderkatan@users.noreply.github.com> Date: Wed, 29 Apr 2026 12:50:49 +0200 Subject: [PATCH 08/12] Add a rudimentary implementation of external collection --- packages/tinyest-for-wgsl/src/parsers.ts | 101 +++++++++--------- .../tinyest-for-wgsl/tests/parsers.test.ts | 17 ++- 2 files changed, 67 insertions(+), 51 deletions(-) diff --git a/packages/tinyest-for-wgsl/src/parsers.ts b/packages/tinyest-for-wgsl/src/parsers.ts index b9697c53ad..947762c6a3 100644 --- a/packages/tinyest-for-wgsl/src/parsers.ts +++ b/packages/tinyest-for-wgsl/src/parsers.ts @@ -37,56 +37,57 @@ function addExternal( ctx: Context, node: babel.ThisExpression | acorn.ThisExpression | babel.Identifier | acorn.Identifier, ) { - const name = node.type === 'Identifier' ? node.name : 'this'; - ctx.externalNames[name] = name; - - // const chain: string[] = []; - - // for (let i = ctx.ancestorChain.length - 1; i >= 0; i--) { - // const current = ctx.ancestorChain[i]; - - // if (!current) { - // break; - // } - - // if (current.type === 'Identifier') { - // chain.push(current.name); - // } else if (current.type === 'MemberExpression' && !current.computed) { - // chain.push(`${(current.property as { name: string }).name}`); - // } else { - // break; - // } - // } - - // let currentExternals = ctx.externalNames; - // for (const elem of chain) { - // let nextExternals = currentExternals.get(elem); - // if (nextExternals) { - // if (typeof nextExternals !== 'string') { - // currentExternals = nextExternals; - // } else { - // // we already need this in externals, so we break - // break; - // } - // } else { - // nextExternals = new Map(); - // currentExternals.set(elem, nextExternals); - // currentExternals = nextExternals; - // } - // } - - // const lastKey = chain[chain.length - 1]; - // if (lastKey !== undefined) { - // let parent = ctx.externalNames; - // for (const key of chain.slice(0, -1)) { - // const next = parent.get(key); - // if (!next || typeof next === 'string') break; - // parent = next; - // } - // if (parent.get(lastKey) instanceof Map) { - // parent.set(lastKey, chain.join('.')); - // } - // } + const chain: string[] = []; + for (let i = ctx.ancestorChain.length - 1; i >= 0; i--) { + const current = ctx.ancestorChain[i]; + + if (!current) { + break; + } + + if (current.type === 'Identifier') { + chain.push(current.name); + } else if (current.type === 'ThisExpression') { + chain.push('this'); + } else if (current.type === 'MemberExpression' && !current.computed) { + chain.push(`${(current.property as { name: string }).name}`); // TODO: better handling of other nodes + } else { + break; + } + } + + let currentExternals = ctx.externalNames; + if (typeof currentExternals !== 'object') { + throw new Error('??'); + } + for (const elem of chain) { + let nextExternals = currentExternals[elem]; + if (nextExternals) { + if (typeof nextExternals !== 'string') { + currentExternals = nextExternals; + } else { + // we already need this in externals, so we break + break; + } + } else { + const newExt = Object.create(null); + currentExternals[elem] = newExt; + currentExternals = newExt; + } + } + + const lastKey = chain[chain.length - 1]; + if (lastKey !== undefined) { + let parent = ctx.externalNames; + for (const key of chain.slice(0, -1)) { + const next = parent[key]; + if (!next || typeof next === 'string') break; + parent = next; + } + if (typeof parent[lastKey] === 'object') { + parent[lastKey] = chain.join('.'); + } + } } const Transpilers: Partial<{ diff --git a/packages/tinyest-for-wgsl/tests/parsers.test.ts b/packages/tinyest-for-wgsl/tests/parsers.test.ts index 16b660653c..59cf0a7fcf 100644 --- a/packages/tinyest-for-wgsl/tests/parsers.test.ts +++ b/packages/tinyest-for-wgsl/tests/parsers.test.ts @@ -226,12 +226,27 @@ describe('transpileFn', () => { const f = ext.s; const g = ext.s.a; + + const h = ext.t.fn().x; + const i = ext.t.comp['computed'].x; }`), ); expect(externalNames).toMatchInlineSnapshot(` { - "ext": "ext", + "ext": { + "p": "ext.p", + "q": { + "a": "ext.q.a", + "b": "ext.q.b", + }, + "r": "ext.r", + "s": "ext.s", + "t": { + "comp": "ext.t.comp", + "fn": "ext.t.fn", + }, + }, } `); }), From 498c97fbab306a622f1e5a5a7ce6646463ed70e0 Mon Sep 17 00:00:00 2001 From: Aleksander Katan <56294622+aleksanderkatan@users.noreply.github.com> Date: Wed, 29 Apr 2026 14:38:43 +0200 Subject: [PATCH 09/12] Update bahbel externals generation --- packages/tinyest-for-wgsl/src/parsers.ts | 1 + .../tinyest-for-wgsl/tests/parsers.test.ts | 6 ++- packages/unplugin-typegpu/src/babel.ts | 41 ++++++++------ .../test/nested-externals.test.ts | 53 +++++++++++-------- 4 files changed, 60 insertions(+), 41 deletions(-) diff --git a/packages/tinyest-for-wgsl/src/parsers.ts b/packages/tinyest-for-wgsl/src/parsers.ts index 947762c6a3..341cdc94c7 100644 --- a/packages/tinyest-for-wgsl/src/parsers.ts +++ b/packages/tinyest-for-wgsl/src/parsers.ts @@ -37,6 +37,7 @@ function addExternal( ctx: Context, node: babel.ThisExpression | acorn.ThisExpression | babel.Identifier | acorn.Identifier, ) { + // TODO: clean up this mess const chain: string[] = []; for (let i = ctx.ancestorChain.length - 1; i >= 0; i--) { const current = ctx.ancestorChain[i]; diff --git a/packages/tinyest-for-wgsl/tests/parsers.test.ts b/packages/tinyest-for-wgsl/tests/parsers.test.ts index 59cf0a7fcf..9290b3774c 100644 --- a/packages/tinyest-for-wgsl/tests/parsers.test.ts +++ b/packages/tinyest-for-wgsl/tests/parsers.test.ts @@ -125,7 +125,11 @@ describe('transpileFn', () => { // Only 'external' is external. expect(externalNames).toMatchInlineSnapshot(` { - "external": "external", + "external": { + "outside": { + "prop": "external.outside.prop", + }, + }, } `); }), diff --git a/packages/unplugin-typegpu/src/babel.ts b/packages/unplugin-typegpu/src/babel.ts index 2fd7d0c21b..fa328d58f0 100644 --- a/packages/unplugin-typegpu/src/babel.ts +++ b/packages/unplugin-typegpu/src/babel.ts @@ -16,6 +16,29 @@ function i(identifier: string): t.Identifier { return t.identifier(identifier); } +interface Externals { + [key: string]: Externals | string; +} + +function externalsToNode(externals: Externals | string): t.Expression { + if (typeof externals === 'string') { + const chain = externals.split('.'); + if (!chain[0]) { + throw new Error('??'); + } + const base = chain[0] === 'this' ? t.thisExpression() : i(chain[0]); + const propAccess = chain + .slice(1) + .reduce((obj, prop) => t.memberExpression(obj, t.identifier(prop)), base); + return t.arrowFunctionExpression([], propAccess); + } + return t.objectExpression( + Object.entries(externals).map(([name, value]) => + t.objectProperty(i(name), externalsToNode(value), false), + ), + ); +} + function assignMetadata( this: PluginState, path: NodePath, @@ -26,23 +49,7 @@ function assignMetadata( t.objectProperty(i('v'), t.numericLiteral(FORMAT_VERSION)), t.objectProperty(i('name'), t.valueToNode(name)), t.objectProperty(i('ast'), t.valueToNode(ast)), - t.objectProperty( - i('externals'), - t.arrowFunctionExpression( - [], - t.blockStatement([ - t.returnStatement( - t.objectExpression( - Object.keys(ast.externalNames).map((name) => - name === 'this' - ? t.objectProperty(i('this'), t.thisExpression()) - : t.objectProperty(i(name), i(name), false, /* shorthand */ name !== 'this'), - ), - ), - ), - ]), - ), - ), + t.objectProperty(i('externals'), externalsToNode(ast.externalNames)), ]); let expression: t.Expression; diff --git a/packages/unplugin-typegpu/test/nested-externals.test.ts b/packages/unplugin-typegpu/test/nested-externals.test.ts index 55dc6ddc17..cb3986eca1 100644 --- a/packages/unplugin-typegpu/test/nested-externals.test.ts +++ b/packages/unplugin-typegpu/test/nested-externals.test.ts @@ -53,6 +53,12 @@ const codes = { 'skips calls': `\ import tgpu, { d } from 'typegpu'; + const fn = () => { + 'use gpu'; + const a = ext.comptime().x; + const b = ext.runtime().y; + }; + const ext = { comptime: tgpu.comptime(() => { return d.vec4f(); @@ -63,12 +69,8 @@ const codes = { }, }; - const fn = () => { - 'use gpu'; - const a = ext.comptime().x; - const b = ext.runtime().y; - }; console.log(fn);`, + // TODO: private access test }; describe('externals gathering', () => { @@ -77,10 +79,14 @@ describe('externals gathering', () => { const code = codes['allows multiple usages of one external']; expect(extractExternals(babelTransform(code))).toMatchInlineSnapshot(` - "() => { - return { - ext - }; + "{ + ext: { + value: () => ext.value, + config: { + multiplier: () => ext.config.multiplier, + zero: () => ext.config.zero + } + } }" `); }); @@ -89,10 +95,12 @@ describe('externals gathering', () => { const code = codes['treats dereference like a regular external']; expect(extractExternals(babelTransform(code))).toMatchInlineSnapshot(` - "() => { - return { - buffer - }; + "{ + buffer: { + $: { + x: () => buffer.$.x + } + } }" `); }); @@ -101,10 +109,8 @@ describe('externals gathering', () => { const code = codes['skips computed prop access']; expect(extractExternals(babelTransform(code))).toMatchInlineSnapshot(` - "() => { - return { - ext - }; + "{ + ext: () => ext }" `); }); @@ -113,11 +119,12 @@ describe('externals gathering', () => { const code = codes['skips calls']; expect(extractExternals(babelTransform(code))).toMatchInlineSnapshot(` - "() => { - return { - d - }; - }" + "{ + ext: { + comptime: () => ext.comptime, + runtime: () => ext.runtime + } + }" `); }); }); @@ -150,7 +157,7 @@ describe('externals gathering', () => { it('skips calls', async () => { const code = codes['skips calls']; - expect(extractExternals(await rollupTransform(code))).toMatchInlineSnapshot(`"() => ({d}),"`); + expect(extractExternals(await rollupTransform(code))).toMatchInlineSnapshot(`"() => ({ext}),"`); }); }); }); From 9551a703e916d7e9e7ba758775e086b6ef739bc0 Mon Sep 17 00:00:00 2001 From: Aleksander Katan <56294622+aleksanderkatan@users.noreply.github.com> Date: Wed, 29 Apr 2026 15:02:28 +0200 Subject: [PATCH 10/12] Update rollup --- .../typegpu/tests/unplugin/autoname.test.ts | 4 +- packages/unplugin-typegpu/src/babel.ts | 5 +- packages/unplugin-typegpu/src/core/common.ts | 4 + packages/unplugin-typegpu/src/core/factory.ts | 17 +- .../unplugin-typegpu/test/aliasing.test.ts | 30 +-- .../unplugin-typegpu/test/auto-naming.test.ts | 108 ++++----- .../test/nested-externals.test.ts | 18 +- .../test/parser-options.test.ts | 10 +- .../test/tgsl-transpiling.test.ts | 107 +++++---- .../test/typescript-syntax.test.ts | 6 +- .../test/use-gpu-directive.test.ts | 220 ++++++++---------- 11 files changed, 258 insertions(+), 271 deletions(-) diff --git a/packages/typegpu/tests/unplugin/autoname.test.ts b/packages/typegpu/tests/unplugin/autoname.test.ts index 45faa7039b..02500ebc65 100644 --- a/packages/typegpu/tests/unplugin/autoname.test.ts +++ b/packages/typegpu/tests/unplugin/autoname.test.ts @@ -196,7 +196,7 @@ describe('autonaming', () => { }), { v: 1, name: "myFun", - ast: {"params":[],"body":[0,[[10,[5,"0"]]]],"externalNames":[]}, + ast: {"params":[],"body":[0,[[10,[5,"0"]]]],"externalNames":{}}, externals: () => ({}), }) && $.f)({})); @@ -206,7 +206,7 @@ describe('autonaming', () => { }), { v: 1, name: undefined, - ast: {"params":[],"body":[0,[[6,"myFun",[]]]],"externalNames":["myFun"]}, + ast: {"params":[],"body":[0,[[6,"myFun",[]]]],"externalNames":{"myFun":"myFun"}}, externals: () => ({myFun}), }) && $.f)({}))), "main")); return main; diff --git a/packages/unplugin-typegpu/src/babel.ts b/packages/unplugin-typegpu/src/babel.ts index fa328d58f0..bbeada6bca 100644 --- a/packages/unplugin-typegpu/src/babel.ts +++ b/packages/unplugin-typegpu/src/babel.ts @@ -4,6 +4,7 @@ import defu from 'defu'; import { transpileFn } from 'tinyest-for-wgsl'; import { FORMAT_VERSION } from 'tinyest'; import { + type Externals, type PluginState, defaultOptions, functionVisitor, @@ -16,10 +17,6 @@ function i(identifier: string): t.Identifier { return t.identifier(identifier); } -interface Externals { - [key: string]: Externals | string; -} - function externalsToNode(externals: Externals | string): t.Expression { if (typeof externals === 'string') { const chain = externals.split('.'); diff --git a/packages/unplugin-typegpu/src/core/common.ts b/packages/unplugin-typegpu/src/core/common.ts index eed49b6184..dde1434d5f 100644 --- a/packages/unplugin-typegpu/src/core/common.ts +++ b/packages/unplugin-typegpu/src/core/common.ts @@ -30,6 +30,10 @@ export interface Options { earlyPruning?: boolean | undefined; } +export interface Externals { + [key: string]: Externals | string; +} + export type MetadatableFunction = | t.FunctionDeclaration | t.FunctionExpression diff --git a/packages/unplugin-typegpu/src/core/factory.ts b/packages/unplugin-typegpu/src/core/factory.ts index 22559354a2..927cc569ef 100644 --- a/packages/unplugin-typegpu/src/core/factory.ts +++ b/packages/unplugin-typegpu/src/core/factory.ts @@ -15,7 +15,7 @@ import { getBlockScope, } from './common.ts'; -import type { Options, UnpluginPluginState, MetadatableFunction } from './common.ts'; +import type { Options, UnpluginPluginState, MetadatableFunction, Externals } from './common.ts'; // I love CommonJS 💔 let traverse = _traverse; @@ -32,19 +32,28 @@ function embedJSON(jsValue: unknown) { .replace(/\u2029/g, '\\u2029'); } +function externalsToString(externals: Externals | string): string { + if (typeof externals === 'string') { + return `() => ${externals}`; + } + const entries = Object.entries(externals).map( + ([key, value]) => `${key}: ${externalsToString(value)}`, + ); + return `{ ${entries.join(', ')} }`; +} + function assignMetadata( this: UnpluginPluginState, path: NodePath, name: string | undefined, ast: ReturnType, ): void { + // TODO: check if we can omit externalNames here const metadata = `{ v: ${FORMAT_VERSION}, name: ${name ? `"${name}"` : 'undefined'}, ast: ${embedJSON(ast)}, - externals: () => ({${Object.keys(ast.externalNames) - .map((e) => (e === 'this' ? '"this": this' : e)) - .join(', ')}}), + externals: ${externalsToString(ast.externalNames)} }`; const visibility = t.isFunctionDeclaration(path.node) diff --git a/packages/unplugin-typegpu/test/aliasing.test.ts b/packages/unplugin-typegpu/test/aliasing.test.ts index 5d07c0c5e7..6be8a600d6 100644 --- a/packages/unplugin-typegpu/test/aliasing.test.ts +++ b/packages/unplugin-typegpu/test/aliasing.test.ts @@ -21,11 +21,9 @@ describe('[BABEL] tgpu alias gathering', () => { ast: { params: [], body: [0, [[13, "x", [1, [5, "2"], "+", [5, "2"]]]]], - externalNames: [] + externalNames: {} }, - externals: () => { - return {}; - } + externals: {} }) && $.f)({}));" `); }); @@ -49,11 +47,9 @@ describe('[BABEL] tgpu alias gathering', () => { ast: { params: [], body: [0, [[13, "x", [1, [5, "2"], "+", [5, "2"]]]]], - externalNames: [] + externalNames: {} }, - externals: () => { - return {}; - } + externals: {} }) && $.f)({}));" `); }); @@ -77,11 +73,9 @@ describe('[BABEL] tgpu alias gathering', () => { ast: { params: [], body: [0, [[13, "x", [1, [5, "2"], "+", [5, "2"]]]]], - externalNames: [] + externalNames: {} }, - externals: () => { - return {}; - } + externals: {} }) && $.f)({}));" `); }); @@ -103,8 +97,8 @@ describe('[ROLLUP] tgpu alias gathering', () => { }), { v: 1, name: undefined, - ast: {"params":[],"body":[0,[]],"externalNames":[]}, - externals: () => ({}), + ast: {"params":[],"body":[0,[]],"externalNames":{}}, + externals: { } }) && $.f)({}))); " `); @@ -126,8 +120,8 @@ describe('[ROLLUP] tgpu alias gathering', () => { }), { v: 1, name: undefined, - ast: {"params":[],"body":[0,[]],"externalNames":[]}, - externals: () => ({}), + ast: {"params":[],"body":[0,[]],"externalNames":{}}, + externals: { } }) && $.f)({}))); " `); @@ -149,8 +143,8 @@ describe('[ROLLUP] tgpu alias gathering', () => { }), { v: 1, name: undefined, - ast: {"params":[],"body":[0,[]],"externalNames":[]}, - externals: () => ({}), + ast: {"params":[],"body":[0,[]],"externalNames":{}}, + externals: { } }) && $.f)({}))); " `); diff --git a/packages/unplugin-typegpu/test/auto-naming.test.ts b/packages/unplugin-typegpu/test/auto-naming.test.ts index b9a1f01bf8..94b7fb600e 100644 --- a/packages/unplugin-typegpu/test/auto-naming.test.ts +++ b/packages/unplugin-typegpu/test/auto-naming.test.ts @@ -27,11 +27,9 @@ describe('[BABEL] auto naming', () => { ast: { params: [], body: [0, []], - externalNames: [] + externalNames: {} }, - externals: () => { - return {}; - } + externals: {} }) && $.f)({})), "fn"); let shell = /*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.fn([]), "shell"); const cst = /*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.const(d.u32, 1), "cst"); @@ -133,11 +131,9 @@ describe('[BABEL] auto naming', () => { ast: { params: [], body: [0, [[10, [5, "0"]]]], - externalNames: [] + externalNames: {} }, - externals: () => { - return {}; - } + externals: {} }) && $.f)({})), "myFunction"); const myComputeFn = /*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.computeFn({ workgroupSize: [1] @@ -147,11 +143,9 @@ describe('[BABEL] auto naming', () => { ast: { params: [], body: [0, []], - externalNames: [] + externalNames: {} }, - externals: () => { - return {}; - } + externals: {} }) && $.f)({})), "myComputeFn"); const myVertexFn = /*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.vertexFn({ out: { @@ -167,11 +161,9 @@ describe('[BABEL] auto naming', () => { body: [0, [[10, [104, { ret: [5, "0"] }]]]], - externalNames: [] + externalNames: {} }, - externals: () => { - return {}; - } + externals: {} }) && $.f)({})), "myVertexFn"); const myFragmentFn = /*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.fragmentFn({ in: { @@ -184,12 +176,16 @@ describe('[BABEL] auto naming', () => { ast: { params: [], body: [0, [[10, [6, [7, "d", "vec4f"], []]]]], - externalNames: ["d"] + externalNames: { + d: { + vec4f: "d.vec4f" + } + } }, - externals: () => { - return { - d - }; + externals: { + d: { + vec4f: () => d.vec4f + } } }) && $.f)({})), "myFragmentFn");" `); @@ -336,11 +332,9 @@ describe('[BABEL] auto naming', () => { ast: { params: [], body: [0, [[10, [5, "0"]]]], - externalNames: [] + externalNames: {} }, - externals: () => { - return {}; - } + externals: {} }) && $.f)({}); const myFun1 = /*#__PURE__*/($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = () => { 'use gpu'; @@ -352,11 +346,9 @@ describe('[BABEL] auto naming', () => { ast: { params: [], body: [0, [[10, [5, "0"]]]], - externalNames: [] + externalNames: {} }, - externals: () => { - return {}; - } + externals: {} }) && $.f)({}); const myFun2 = /*#__PURE__*/($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = function () { 'use gpu'; @@ -368,11 +360,9 @@ describe('[BABEL] auto naming', () => { ast: { params: [], body: [0, [[10, [5, "0"]]]], - externalNames: [] + externalNames: {} }, - externals: () => { - return {}; - } + externals: {} }) && $.f)({});" `); }); @@ -518,11 +508,9 @@ describe('[BABEL] auto naming', () => { ast: { params: [], body: [0, []], - externalNames: [] + externalNames: {} }, - externals: () => { - return {}; - } + externals: {} }) && $.f)({})), "myGuardedPipeline"); const anotherGuardedPipeline = /*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(root.createGuardedComputePipeline(/*#__PURE__*/($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = () => { 'use gpu'; @@ -532,11 +520,9 @@ describe('[BABEL] auto naming', () => { ast: { params: [], body: [0, []], - externalNames: [] + externalNames: {} }, - externals: () => { - return {}; - } + externals: {} }) && $.f)({})).dispatchThreads(), "anotherGuardedPipeline"); console.log(myGuardedPipeline, anotherGuardedPipeline);" `); @@ -569,8 +555,8 @@ describe('[ROLLUP] auto naming', () => { (/*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.fn([])((/*#__PURE__*/($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => {}), { v: 1, name: undefined, - ast: {"params":[],"body":[0,[]],"externalNames":[]}, - externals: () => ({}), + ast: {"params":[],"body":[0,[]],"externalNames":{}}, + externals: { } }) && $.f)({}))), "fn")); (/*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.accessor(d.u32), "accessor")); (/*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.const(d.u32, 1), "cst")); @@ -653,23 +639,23 @@ describe('[ROLLUP] auto naming', () => { (/*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.fn([])((/*#__PURE__*/($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => 0), { v: 1, name: undefined, - ast: {"params":[],"body":[0,[[10,[5,"0"]]]],"externalNames":[]}, - externals: () => ({}), + ast: {"params":[],"body":[0,[[10,[5,"0"]]]],"externalNames":{}}, + externals: { } }) && $.f)({}))), "myFunction")); (/*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.computeFn({ workgroupSize: [1] })( (/*#__PURE__*/($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => {}), { v: 1, name: undefined, - ast: {"params":[],"body":[0,[]],"externalNames":[]}, - externals: () => ({}), + ast: {"params":[],"body":[0,[]],"externalNames":{}}, + externals: { } }) && $.f)({})), ), "myComputeFn")); (/*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.vertexFn({ out: { ret: d.i32 } })( (/*#__PURE__*/($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => ({ ret: 0 })), { v: 1, name: undefined, - ast: {"params":[],"body":[0,[[10,[104,{"ret":[5,"0"]}]]]],"externalNames":[]}, - externals: () => ({}), + ast: {"params":[],"body":[0,[[10,[104,{"ret":[5,"0"]}]]]],"externalNames":{}}, + externals: { } }) && $.f)({})), ), "myVertexFn")); (/*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.fragmentFn({ @@ -679,8 +665,8 @@ describe('[ROLLUP] auto naming', () => { (/*#__PURE__*/($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => d.vec4f()), { v: 1, name: undefined, - ast: {"params":[],"body":[0,[[10,[6,[7,"d","vec4f"],[]]]]],"externalNames":["d"]}, - externals: () => ({d}), + ast: {"params":[],"body":[0,[[10,[6,[7,"d","vec4f"],[]]]]],"externalNames":{"d":{"vec4f":"d.vec4f"}}}, + externals: { d: { vec4f: () => d.vec4f } } }) && $.f)({})), ), "myFragmentFn")); " @@ -834,8 +820,8 @@ describe('[ROLLUP] auto naming', () => { }), { v: 1, name: "myFun3", - ast: {"params":[],"body":[0,[[10,[5,"0"]]]],"externalNames":[]}, - externals: () => ({}), + ast: {"params":[],"body":[0,[[10,[5,"0"]]]],"externalNames":{}}, + externals: { } }) && $.f)({})); const myFun1 = (/*#__PURE__*/($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => { @@ -844,8 +830,8 @@ describe('[ROLLUP] auto naming', () => { }), { v: 1, name: "myFun1", - ast: {"params":[],"body":[0,[[10,[5,"0"]]]],"externalNames":[]}, - externals: () => ({}), + ast: {"params":[],"body":[0,[[10,[5,"0"]]]],"externalNames":{}}, + externals: { } }) && $.f)({})); const myFun2 = (/*#__PURE__*/($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (function () { @@ -854,8 +840,8 @@ describe('[ROLLUP] auto naming', () => { }), { v: 1, name: "myFun2", - ast: {"params":[],"body":[0,[[10,[5,"0"]]]],"externalNames":[]}, - externals: () => ({}), + ast: {"params":[],"body":[0,[[10,[5,"0"]]]],"externalNames":{}}, + externals: { } }) && $.f)({})); @@ -1031,8 +1017,8 @@ describe('[ROLLUP] auto naming', () => { }), { v: 1, name: undefined, - ast: {"params":[],"body":[0,[]],"externalNames":[]}, - externals: () => ({}), + ast: {"params":[],"body":[0,[]],"externalNames":{}}, + externals: { } }) && $.f)({}))), "myGuardedPipeline")); const anotherGuardedPipeline = (/*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(root @@ -1041,8 +1027,8 @@ describe('[ROLLUP] auto naming', () => { }), { v: 1, name: undefined, - ast: {"params":[],"body":[0,[]],"externalNames":[]}, - externals: () => ({}), + ast: {"params":[],"body":[0,[]],"externalNames":{}}, + externals: { } }) && $.f)({}))) .dispatchThreads(), "anotherGuardedPipeline")); diff --git a/packages/unplugin-typegpu/test/nested-externals.test.ts b/packages/unplugin-typegpu/test/nested-externals.test.ts index cb3986eca1..9a3d3acf53 100644 --- a/packages/unplugin-typegpu/test/nested-externals.test.ts +++ b/packages/unplugin-typegpu/test/nested-externals.test.ts @@ -19,25 +19,25 @@ const codes = { zero: 0, } }; - const foo = () => { + const fn = () => { 'use gpu'; const a = ext.value; const b = ext.config.multiplier; const c = ext.config.zero; const d = ext.config.multiplier; }; - console.log(foo);`, + console.log(fn);`, // --- 'treats dereference like a regular external': `\ import tgpu, { d } from 'typegpu'; const root = await tgpu.init(); const buffer = root.createMutable(d.vec2u); - const foo = () => { + const fn = () => { 'use gpu'; const a = buffer.$.x; }; - console.log(foo);`, + console.log(fn);`, // --- 'skips computed prop access': `\ const ext = { @@ -134,7 +134,7 @@ describe('externals gathering', () => { const code = codes['allows multiple usages of one external']; expect(extractExternals(await rollupTransform(code))).toMatchInlineSnapshot( - `"() => ({ext}),"`, + `"{ ext: { value: () => ext.value, config: { multiplier: () => ext.config.multiplier, zero: () => ext.config.zero } } }"`, ); }); @@ -142,7 +142,7 @@ describe('externals gathering', () => { const code = codes['treats dereference like a regular external']; expect(extractExternals(await rollupTransform(code))).toMatchInlineSnapshot( - `"() => ({buffer}),"`, + `"{ buffer: { $: { x: () => buffer.$.x } } }"`, ); }); @@ -150,14 +150,16 @@ describe('externals gathering', () => { const code = codes['skips computed prop access']; expect(extractExternals(await rollupTransform(code))).toMatchInlineSnapshot( - `"() => ({ext}),"`, + `"{ ext: () => ext }"`, ); }); it('skips calls', async () => { const code = codes['skips calls']; - expect(extractExternals(await rollupTransform(code))).toMatchInlineSnapshot(`"() => ({ext}),"`); + expect(extractExternals(await rollupTransform(code))).toMatchInlineSnapshot( + `"{ ext: { comptime: () => ext.comptime, runtime: () => ext.runtime } }"`, + ); }); }); }); diff --git a/packages/unplugin-typegpu/test/parser-options.test.ts b/packages/unplugin-typegpu/test/parser-options.test.ts index 76225b0695..444df9d924 100644 --- a/packages/unplugin-typegpu/test/parser-options.test.ts +++ b/packages/unplugin-typegpu/test/parser-options.test.ts @@ -21,11 +21,9 @@ describe('[BABEL] parser options', () => { ast: { params: [], body: [0, [[13, "x", [1, [5, "2"], "+", [5, "2"]]]]], - externalNames: [] + externalNames: {} }, - externals: () => { - return {}; - } + externals: {} }) && $.f)({}));" `); @@ -61,8 +59,8 @@ describe('[ROLLUP] tgpu alias gathering', async () => { }), { v: 1, name: undefined, - ast: {"params":[],"body":[0,[]],"externalNames":[]}, - externals: () => ({}), + ast: {"params":[],"body":[0,[]],"externalNames":{}}, + externals: { } }) && $.f)({}))); console.log(increment); diff --git a/packages/unplugin-typegpu/test/tgsl-transpiling.test.ts b/packages/unplugin-typegpu/test/tgsl-transpiling.test.ts index 3ba05a8b9b..4ee152b384 100644 --- a/packages/unplugin-typegpu/test/tgsl-transpiling.test.ts +++ b/packages/unplugin-typegpu/test/tgsl-transpiling.test.ts @@ -45,13 +45,30 @@ describe('[BABEL] plugin for transpiling tgsl functions to tinyest', () => { name: "input" }], body: [0, [[13, "tmp", [7, [7, "counter", "$"], "x"]], [2, [7, [7, "counter", "$"], "x"], "=", [7, [7, "counter", "$"], "y"]], [2, [7, [7, "counter", "$"], "y"], "+=", "tmp"], [2, [7, [7, "counter", "$"], "z"], "+=", [6, [7, "d", "f32"], [[7, [7, "input", "num"], "x"]]]]]], - externalNames: ["counter", "d"] + externalNames: { + counter: { + $: { + x: "counter.$.x", + y: "counter.$.y", + z: "counter.$.z" + } + }, + d: { + f32: "d.f32" + } + } }, - externals: () => { - return { - counter, - d - }; + externals: { + counter: { + $: { + x: () => counter.$.x, + y: () => counter.$.y, + z: () => counter.$.z + } + }, + d: { + f32: () => d.f32 + } } }) && $.f)({}));" `); @@ -90,11 +107,9 @@ describe('[BABEL] plugin for transpiling tgsl functions to tinyest', () => { name: "input" }], body: [0, [[13, "x", true]]], - externalNames: [] + externalNames: {} }, - externals: () => { - return {}; - } + externals: {} }) && $.f)({})); const b = tgpu.fn([])(/*#__PURE__*/($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = () => { const y = 2 + 2; @@ -104,11 +119,9 @@ describe('[BABEL] plugin for transpiling tgsl functions to tinyest', () => { ast: { params: [], body: [0, [[13, "y", [1, [5, "2"], "+", [5, "2"]]]]], - externalNames: [] + externalNames: {} }, - externals: () => { - return {}; - } + externals: {} }) && $.f)({})); const cx = 2; const c = tgpu.fn([])(/*#__PURE__*/($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = () => cx, { @@ -117,12 +130,12 @@ describe('[BABEL] plugin for transpiling tgsl functions to tinyest', () => { ast: { params: [], body: [0, [[10, "cx"]]], - externalNames: ["cx"] + externalNames: { + cx: "cx" + } }, - externals: () => { - return { - cx - }; + externals: { + cx: () => cx } }) && $.f)({})); const d = tgpu.fn([])('() {}');" @@ -176,11 +189,9 @@ describe('[BABEL] plugin for transpiling tgsl functions to tinyest', () => { name: "input" }], body: [0, [[13, "x", true]]], - externalNames: [] + externalNames: {} }, - externals: () => { - return {}; - } + externals: {} }) && $.f)({})); const funcWithAs = tgpu.computeFn({ workgroupSize: [1] @@ -195,11 +206,9 @@ describe('[BABEL] plugin for transpiling tgsl functions to tinyest', () => { name: "input" }], body: [0, [[13, "x", true]]], - externalNames: [] + externalNames: {} }, - externals: () => { - return {}; - } + externals: {} }) && $.f)({})); const funcWithSatisfies = tgpu.computeFn({ workgroupSize: [1] @@ -214,11 +223,9 @@ describe('[BABEL] plugin for transpiling tgsl functions to tinyest', () => { name: "input" }], body: [0, [[13, "x", true]]], - externalNames: [] + externalNames: {} }, - externals: () => { - return {}; - } + externals: {} }) && $.f)({}));" `); }); @@ -255,12 +262,20 @@ describe('[BABEL] plugin for transpiling tgsl functions to tinyest', () => { ast: { params: [], body: [0, [[10, [7, [7, "this", "myBuffer"], "$"]]]], - externalNames: ["this"] + externalNames: { + "this": { + myBuffer: { + $: "this.myBuffer.$" + } + } + } }, - externals: () => { - return { - this: this - }; + externals: { + this: { + myBuffer: { + $: () => this.myBuffer.$ + } + } } }) && $.f)({})); } @@ -308,8 +323,8 @@ describe('[ROLLUP] plugin for transpiling tgsl functions to tinyest', () => { }), { v: 1, name: undefined, - ast: {"params":[{"type":"i","name":"input"}],"body":[0,[[13,"tmp",[7,[7,"counter","$"],"x"]],[2,[7,[7,"counter","$"],"x"],"=",[7,[7,"counter","$"],"y"]],[2,[7,[7,"counter","$"],"y"],"+=","tmp"],[2,[7,[7,"counter","$"],"z"],"+=",[6,[7,"d","f32"],[[7,[7,"input","num"],"x"]]]]]],"externalNames":["counter","d"]}, - externals: () => ({counter, d}), + ast: {"params":[{"type":"i","name":"input"}],"body":[0,[[13,"tmp",[7,[7,"counter","$"],"x"]],[2,[7,[7,"counter","$"],"x"],"=",[7,[7,"counter","$"],"y"]],[2,[7,[7,"counter","$"],"y"],"+=","tmp"],[2,[7,[7,"counter","$"],"z"],"+=",[6,[7,"d","f32"],[[7,[7,"input","num"],"x"]]]]]],"externalNames":{"counter":{"$":{"x":"counter.$.x","y":"counter.$.y","z":"counter.$.z"}},"d":{"f32":"d.f32"}}}, + externals: { counter: { $: { x: () => counter.$.x, y: () => counter.$.y, z: () => counter.$.z } }, d: { f32: () => d.f32 } } }) && $.f)({}))); " `); @@ -340,24 +355,24 @@ describe('[ROLLUP] plugin for transpiling tgsl functions to tinyest', () => { }), { v: 1, name: undefined, - ast: {"params":[{"type":"i","name":"input"}],"body":[0,[[13,"x",true]]],"externalNames":[]}, - externals: () => ({}), + ast: {"params":[{"type":"i","name":"input"}],"body":[0,[[13,"x",true]]],"externalNames":{}}, + externals: { } }) && $.f)({}))); tgpu.fn([])((/*#__PURE__*/($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => { }), { v: 1, name: undefined, - ast: {"params":[],"body":[0,[[13,"y",[1,[5,"2"],"+",[5,"2"]]]]],"externalNames":[]}, - externals: () => ({}), + ast: {"params":[],"body":[0,[[13,"y",[1,[5,"2"],"+",[5,"2"]]]]],"externalNames":{}}, + externals: { } }) && $.f)({}))); const cx = 2; tgpu.fn([])((/*#__PURE__*/($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => cx), { v: 1, name: undefined, - ast: {"params":[],"body":[0,[[10,"cx"]]],"externalNames":["cx"]}, - externals: () => ({cx}), + ast: {"params":[],"body":[0,[[10,"cx"]]],"externalNames":{"cx":"cx"}}, + externals: { cx: () => cx } }) && $.f)({}))); tgpu.fn([])('() {}'); @@ -413,8 +428,8 @@ describe('[ROLLUP] plugin for transpiling tgsl functions to tinyest', () => { }), { v: 1, name: undefined, - ast: {"params":[],"body":[0,[[10,[7,[7,"this","myBuffer"],"$"]]]],"externalNames":["this"]}, - externals: () => ({"this": this}), + ast: {"params":[],"body":[0,[[10,[7,[7,"this","myBuffer"],"$"]]]],"externalNames":{"this":{"myBuffer":{"$":"this.myBuffer.$"}}}}, + externals: { this: { myBuffer: { $: () => this.myBuffer.$ } } } }) && $.f)({}))); } diff --git a/packages/unplugin-typegpu/test/typescript-syntax.test.ts b/packages/unplugin-typegpu/test/typescript-syntax.test.ts index b6af6fbff9..44dd8f6a77 100644 --- a/packages/unplugin-typegpu/test/typescript-syntax.test.ts +++ b/packages/unplugin-typegpu/test/typescript-syntax.test.ts @@ -29,11 +29,9 @@ describe('as type', () => { name: "b" }], body: [0, [[10, [1, "a", "+", "b"]]]], - externalNames: [] + externalNames: {} }, - externals: () => { - return {}; - } + externals: {} }) && $.f)({});" `); }); diff --git a/packages/unplugin-typegpu/test/use-gpu-directive.test.ts b/packages/unplugin-typegpu/test/use-gpu-directive.test.ts index 9a32b06b5a..1c18711ac9 100644 --- a/packages/unplugin-typegpu/test/use-gpu-directive.test.ts +++ b/packages/unplugin-typegpu/test/use-gpu-directive.test.ts @@ -37,11 +37,9 @@ describe('"use gpu" marked arrow function, assigned to a const', () => { name: "b" }], body: [0, [[10, [1, "a", "+", "b"]]]], - externalNames: [] + externalNames: {} }, - externals: () => { - return {}; - } + externals: {} }) && $.f)({}); const addCPU = (a, b) => { return a + b; @@ -60,8 +58,8 @@ describe('"use gpu" marked arrow function, assigned to a const', () => { }), { v: 1, name: "addGPU", - ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":[]}, - externals: () => ({}), + ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":{}}, + externals: { } }) && $.f)({})); const addCPU = (a, b) => { @@ -110,11 +108,9 @@ describe('marked arrow functions passed to shells', () => { name: "b" }], body: [0, [[10, [1, "a", "+", "b"]]]], - externalNames: [] + externalNames: {} }, - externals: () => { - return {}; - } + externals: {} }) && $.f)({})); shell((a, b) => { return a + b; @@ -134,8 +130,8 @@ describe('marked arrow functions passed to shells', () => { }), { v: 1, name: undefined, - ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":[]}, - externals: () => ({}), + ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":{}}, + externals: { } }) && $.f)({}))); shell((a, b) => { @@ -182,11 +178,9 @@ describe('marked anonymous function expressions passed to shells', () => { name: "b" }], body: [0, [[10, [1, "a", "+", "b"]]]], - externalNames: [] + externalNames: {} }, - externals: () => { - return {}; - } + externals: {} }) && $.f)({})); shell(function (a, b) { return a + b; @@ -206,8 +200,8 @@ describe('marked anonymous function expressions passed to shells', () => { }), { v: 1, name: undefined, - ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":[]}, - externals: () => ({}), + ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":{}}, + externals: { } }) && $.f)({}))); shell(function(a, b) { @@ -254,11 +248,9 @@ describe('marked named function expressions passed to shells', () => { name: "b" }], body: [0, [[10, [1, "a", "+", "b"]]]], - externalNames: [] + externalNames: {} }, - externals: () => { - return {}; - } + externals: {} }) && $.f)({})); shell(function addCPU(a, b) { return a + b; @@ -278,8 +270,8 @@ describe('marked named function expressions passed to shells', () => { }), { v: 1, name: "addGPU", - ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":[]}, - externals: () => ({}), + ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":{}}, + externals: { } }) && $.f)({}))); shell(function addCPU(a, b) { @@ -327,11 +319,9 @@ describe('marked function statements', () => { name: "b" }], body: [0, [[10, [1, "a", "+", "b"]]]], - externalNames: [] + externalNames: {} }, - externals: () => { - return {}; - } + externals: {} }) && $.f)({}); function addCPU(a, b) { return a + b; @@ -350,8 +340,8 @@ describe('marked function statements', () => { }), { v: 1, name: "addGPU", - ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":[]}, - externals: () => ({}), + ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":{}}, + externals: { } }) && $.f)({})); @@ -415,11 +405,9 @@ describe('marked object methods', () => { name: "b" }], body: [0, [[10, [1, "a", "%", "b"]]]], - externalNames: [] + externalNames: {} }, - externals: () => { - return {}; - } + externals: {} }) && $.f)({}) }; @@ -445,12 +433,16 @@ describe('marked object methods', () => { name: "n" }], body: [0, [[11, [1, "n", "<=", [5, "1"]], [0, [[10, false]]]], [14, [12, "i", [5, "2"]], [1, "i", "<", "n"], [102, "++", "i"], [0, [[11, [1, [6, [7, "obj", "mod"], ["n", "i"]], "===", [5, "0"]], [0, [[10, false]]]]]]], [10, true]]], - externalNames: ["obj"] + externalNames: { + obj: { + mod: "obj.mod" + } + } }, - externals: () => { - return { - obj - }; + externals: { + obj: { + mod: () => obj.mod + } } }) && $.f)({}); console.log(obj, isPrime);" @@ -467,8 +459,8 @@ describe('marked object methods', () => { }), { v: 1, name: "mod", - ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","%","b"]]]],"externalNames":[]}, - externals: () => ({}), + ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","%","b"]]]],"externalNames":{}}, + externals: { } }) && $.f)({})) }; @@ -488,8 +480,8 @@ describe('marked object methods', () => { }), { v: 1, name: "isPrime", - ast: {"params":[{"type":"i","name":"n"}],"body":[0,[[11,[1,"n","<=",[5,"1"]],[0,[[10,false]]]],[14,[12,"i",[5,"2"]],[1,"i","<","n"],[102,"++","i"],[0,[[11,[1,[6,[7,"obj","mod"],["n","i"]],"===",[5,"0"]],[0,[[10,false]]]]]]],[10,true]]],"externalNames":["obj"]}, - externals: () => ({obj}), + ast: {"params":[{"type":"i","name":"n"}],"body":[0,[[11,[1,"n","<=",[5,"1"]],[0,[[10,false]]]],[14,[12,"i",[5,"2"]],[1,"i","<","n"],[102,"++","i"],[0,[[11,[1,[6,[7,"obj","mod"],["n","i"]],"===",[5,"0"]],[0,[[10,false]]]]]]],[10,true]]],"externalNames":{"obj":{"mod":"obj.mod"}}}, + externals: { obj: { mod: () => obj.mod } } }) && $.f)({})); console.log(obj, isPrime); @@ -543,12 +535,16 @@ describe('transforms numeric operations', () => { name: "b" }], body: [0, [[12, "c", [1, [1, "a", "+", "b"], "+", [5, "2"]]], [2, "c", "+=", [1, [5, "2"], "*", "b"]], [2, [7, "countMutable", "$"], "+=", [5, "3"]]]], - externalNames: ["countMutable"] + externalNames: { + countMutable: { + $: "countMutable.$" + } + } }, - externals: () => { - return { - countMutable - }; + externals: { + countMutable: { + $: () => countMutable.$ + } } }) && $.f)({}); console.log(main);" @@ -572,8 +568,8 @@ describe('transforms numeric operations', () => { }), { v: 1, name: "main", - ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[12,"c",[1,[1,"a","+","b"],"+",[5,"2"]]],[2,"c","+=",[1,[5,"2"],"*","b"]],[2,[7,"countMutable","$"],"+=",[5,"3"]]]],"externalNames":["countMutable"]}, - externals: () => ({countMutable}), + ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[12,"c",[1,[1,"a","+","b"],"+",[5,"2"]]],[2,"c","+=",[1,[5,"2"],"*","b"]],[2,[7,"countMutable","$"],"+=",[5,"3"]]]],"externalNames":{"countMutable":{"$":"countMutable.$"}}}, + externals: { countMutable: { $: () => countMutable.$ } } }) && $.f)({})); console.log(main); @@ -622,11 +618,9 @@ describe('hoists global function statements marked with "use gpu"', () => { name: "b" }], body: [0, [[10, [1, "a", "*", "b"]]]], - externalNames: [] + externalNames: {} }, - externals: () => { - return {}; - } + externals: {} }) && $.f)({}); /** ADD */ // another comment @@ -646,11 +640,9 @@ describe('hoists global function statements marked with "use gpu"', () => { name: "b" }], body: [0, [[10, [1, "a", "+", "b"]]]], - externalNames: [] + externalNames: {} }, - externals: () => { - return {}; - } + externals: {} }) && $.f)({}); console.log(add, mul);" `); @@ -666,8 +658,8 @@ describe('hoists global function statements marked with "use gpu"', () => { }), { v: 1, name: "mul", - ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","*","b"]]]],"externalNames":[]}, - externals: () => ({}), + ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","*","b"]]]],"externalNames":{}}, + externals: { } }) && $.f)({})); /** ADD */ @@ -678,8 +670,8 @@ describe('hoists global function statements marked with "use gpu"', () => { }), { v: 1, name: "add", - ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":[]}, - externals: () => ({}), + ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":{}}, + externals: { } }) && $.f)({})); console.log(add, mul); @@ -730,11 +722,9 @@ describe('hoists function statements marked with "use gpu", scoped inside anothe name: "b" }], body: [0, [[10, [1, "a", "*", "b"]]]], - externalNames: [] + externalNames: {} }, - externals: () => { - return {}; - } + externals: {} }) && $.f)({}); /** ADD */ // another comment @@ -754,11 +744,9 @@ describe('hoists function statements marked with "use gpu", scoped inside anothe name: "b" }], body: [0, [[10, [1, "a", "+", "b"]]]], - externalNames: [] + externalNames: {} }, - externals: () => { - return {}; - } + externals: {} }) && $.f)({}); console.log(add, mul); }" @@ -776,8 +764,8 @@ describe('hoists function statements marked with "use gpu", scoped inside anothe }), { v: 1, name: "mul", - ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","*","b"]]]],"externalNames":[]}, - externals: () => ({}), + ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","*","b"]]]],"externalNames":{}}, + externals: { } }) && $.f)({})); /** ADD */ @@ -788,8 +776,8 @@ describe('hoists function statements marked with "use gpu", scoped inside anothe }), { v: 1, name: "add", - ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":[]}, - externals: () => ({}), + ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":{}}, + externals: { } }) && $.f)({})); console.log(add, mul); @@ -851,11 +839,9 @@ describe('hoists function statements marked with "use gpu", scoped inside an arr name: "b" }], body: [0, [[10, [1, "a", "*", "b"]]]], - externalNames: [] + externalNames: {} }, - externals: () => { - return {}; - } + externals: {} }) && $.f)({}); /** ADD */ // another comment @@ -875,11 +861,9 @@ describe('hoists function statements marked with "use gpu", scoped inside an arr name: "b" }], body: [0, [[10, [1, "a", "+", "b"]]]], - externalNames: [] + externalNames: {} }, - externals: () => { - return {}; - } + externals: {} }) && $.f)({}); console.log(add, mul); };" @@ -897,8 +881,8 @@ describe('hoists function statements marked with "use gpu", scoped inside an arr }), { v: 1, name: "mul", - ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","*","b"]]]],"externalNames":[]}, - externals: () => ({}), + ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","*","b"]]]],"externalNames":{}}, + externals: { } }) && $.f)({})); /** ADD */ @@ -909,8 +893,8 @@ describe('hoists function statements marked with "use gpu", scoped inside an arr }), { v: 1, name: "add", - ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":[]}, - externals: () => ({}), + ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":{}}, + externals: { } }) && $.f)({})); console.log(add, mul); @@ -974,12 +958,12 @@ describe('hoists function statements marked with "use gpu", scoped inside an if name: "b" }], body: [0, [[10, [1, [1, "a", "*", "b"], "*", "c"]]]], - externalNames: ["c"] + externalNames: { + c: "c" + } }, - externals: () => { - return { - c - }; + externals: { + c: () => c } }) && $.f)({}); /** ADD */ @@ -1000,12 +984,12 @@ describe('hoists function statements marked with "use gpu", scoped inside an if name: "b" }], body: [0, [[10, [1, [1, "a", "+", "b"], "+", "c"]]]], - externalNames: ["c"] + externalNames: { + c: "c" + } }, - externals: () => { - return { - c - }; + externals: { + c: () => c } }) && $.f)({}); console.log(add, mul); @@ -1025,8 +1009,8 @@ describe('hoists function statements marked with "use gpu", scoped inside an if }), { v: 1, name: "mul", - ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,[1,"a","*","b"],"*","c"]]]],"externalNames":["c"]}, - externals: () => ({c}), + ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,[1,"a","*","b"],"*","c"]]]],"externalNames":{"c":"c"}}, + externals: { c: () => c } }) && $.f)({})); /** ADD */ @@ -1037,8 +1021,8 @@ describe('hoists function statements marked with "use gpu", scoped inside an if }), { v: 1, name: "add", - ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,[1,"a","+","b"],"+","c"]]]],"externalNames":["c"]}, - externals: () => ({c}), + ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,[1,"a","+","b"],"+","c"]]]],"externalNames":{"c":"c"}}, + externals: { c: () => c } }) && $.f)({})); console.log(add, mul); @@ -1106,12 +1090,12 @@ describe('replaces function statements marked with "use gpu" in place when condi name: "b" }], body: [0, [[10, [1, [1, "a", "+", "b"], "+", "c"]]]], - externalNames: ["c"] + externalNames: { + c: "c" + } }, - externals: () => { - return { - c - }; + externals: { + c: () => c } }) && $.f)({}); break; @@ -1134,12 +1118,12 @@ describe('replaces function statements marked with "use gpu" in place when condi name: "b" }], body: [0, [[10, [1, [1, "a", "*", "b"], "*", "c"]]]], - externalNames: ["c"] + externalNames: { + c: "c" + } }, - externals: () => { - return { - c - }; + externals: { + c: () => c } }) && $.f)({}); break; @@ -1162,8 +1146,8 @@ describe('replaces function statements marked with "use gpu" in place when condi }), { v: 1, name: "add", - ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,[1,"a","+","b"],"+","c"]]]],"externalNames":["c"]}, - externals: () => ({c}), + ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,[1,"a","+","b"],"+","c"]]]],"externalNames":{"c":"c"}}, + externals: { c: () => c } }) && $.f)({})); @@ -1177,8 +1161,8 @@ describe('replaces function statements marked with "use gpu" in place when condi }), { v: 1, name: "mul", - ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,[1,"a","*","b"],"*","c"]]]],"externalNames":["c"]}, - externals: () => ({c}), + ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,[1,"a","*","b"],"*","c"]]]],"externalNames":{"c":"c"}}, + externals: { c: () => c } }) && $.f)({})); @@ -1216,8 +1200,8 @@ test('hoists exported marked function statements', async () => { }), { v: 1, name: "mul", - ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","*","b"]]]],"externalNames":[]}, - externals: () => ({}), + ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","*","b"]]]],"externalNames":{}}, + externals: { } }) && $.f)({})); /** ADD */ @@ -1227,8 +1211,8 @@ test('hoists exported marked function statements', async () => { }), { v: 1, name: "add", - ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":[]}, - externals: () => ({}), + ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":{}}, + externals: { } }) && $.f)({})); console.log(add); From 7a3756db2faccbffde89a27ed9e9574bf50a7d17 Mon Sep 17 00:00:00 2001 From: Aleksander Katan <56294622+aleksanderkatan@users.noreply.github.com> Date: Wed, 29 Apr 2026 15:34:08 +0200 Subject: [PATCH 11/12] Save new externals to externals2 instead --- .../typegpu/tests/unplugin/autoname.test.ts | 2 + packages/unplugin-typegpu/src/babel.ts | 19 +- packages/unplugin-typegpu/src/core/factory.ts | 5 +- .../unplugin-typegpu/test/aliasing.test.ts | 24 ++- .../unplugin-typegpu/test/auto-naming.test.ts | 82 ++++++--- .../test/nested-externals.test.ts | 48 +++++- .../test/parser-options.test.ts | 8 +- .../test/tgsl-transpiling.test.ts | 62 +++++-- .../test/typescript-syntax.test.ts | 5 +- .../test/use-gpu-directive.test.ts | 162 ++++++++++++++---- 10 files changed, 327 insertions(+), 90 deletions(-) diff --git a/packages/typegpu/tests/unplugin/autoname.test.ts b/packages/typegpu/tests/unplugin/autoname.test.ts index 02500ebc65..17f525fb0e 100644 --- a/packages/typegpu/tests/unplugin/autoname.test.ts +++ b/packages/typegpu/tests/unplugin/autoname.test.ts @@ -198,6 +198,7 @@ describe('autonaming', () => { name: "myFun", ast: {"params":[],"body":[0,[[10,[5,"0"]]]],"externalNames":{}}, externals: () => ({}), + externals2: { } }) && $.f)({})); @@ -208,6 +209,7 @@ describe('autonaming', () => { name: undefined, ast: {"params":[],"body":[0,[[6,"myFun",[]]]],"externalNames":{"myFun":"myFun"}}, externals: () => ({myFun}), + externals2: { myFun: () => myFun } }) && $.f)({}))), "main")); return main; }" diff --git a/packages/unplugin-typegpu/src/babel.ts b/packages/unplugin-typegpu/src/babel.ts index bbeada6bca..5c2470d053 100644 --- a/packages/unplugin-typegpu/src/babel.ts +++ b/packages/unplugin-typegpu/src/babel.ts @@ -46,7 +46,24 @@ function assignMetadata( t.objectProperty(i('v'), t.numericLiteral(FORMAT_VERSION)), t.objectProperty(i('name'), t.valueToNode(name)), t.objectProperty(i('ast'), t.valueToNode(ast)), - t.objectProperty(i('externals'), externalsToNode(ast.externalNames)), + t.objectProperty( + i('externals'), + t.arrowFunctionExpression( + [], + t.blockStatement([ + t.returnStatement( + t.objectExpression( + Object.keys(ast.externalNames).map((name) => + name === 'this' + ? t.objectProperty(i('this'), t.thisExpression()) + : t.objectProperty(i(name), i(name), false, /* shorthand */ name !== 'this'), + ), + ), + ), + ]), + ), + ), + t.objectProperty(i('externals2'), externalsToNode(ast.externalNames)), ]); let expression: t.Expression; diff --git a/packages/unplugin-typegpu/src/core/factory.ts b/packages/unplugin-typegpu/src/core/factory.ts index 927cc569ef..6b9d429b15 100644 --- a/packages/unplugin-typegpu/src/core/factory.ts +++ b/packages/unplugin-typegpu/src/core/factory.ts @@ -53,7 +53,10 @@ function assignMetadata( v: ${FORMAT_VERSION}, name: ${name ? `"${name}"` : 'undefined'}, ast: ${embedJSON(ast)}, - externals: ${externalsToString(ast.externalNames)} + externals: () => ({${Object.keys(ast.externalNames) + .map((e) => (e === 'this' ? '"this": this' : e)) + .join(', ')}}), + externals2: ${externalsToString(ast.externalNames)} }`; const visibility = t.isFunctionDeclaration(path.node) diff --git a/packages/unplugin-typegpu/test/aliasing.test.ts b/packages/unplugin-typegpu/test/aliasing.test.ts index 6be8a600d6..fc08b76f9a 100644 --- a/packages/unplugin-typegpu/test/aliasing.test.ts +++ b/packages/unplugin-typegpu/test/aliasing.test.ts @@ -23,7 +23,10 @@ describe('[BABEL] tgpu alias gathering', () => { body: [0, [[13, "x", [1, [5, "2"], "+", [5, "2"]]]]], externalNames: {} }, - externals: {} + externals: () => { + return {}; + }, + externals2: {} }) && $.f)({}));" `); }); @@ -49,7 +52,10 @@ describe('[BABEL] tgpu alias gathering', () => { body: [0, [[13, "x", [1, [5, "2"], "+", [5, "2"]]]]], externalNames: {} }, - externals: {} + externals: () => { + return {}; + }, + externals2: {} }) && $.f)({}));" `); }); @@ -75,7 +81,10 @@ describe('[BABEL] tgpu alias gathering', () => { body: [0, [[13, "x", [1, [5, "2"], "+", [5, "2"]]]]], externalNames: {} }, - externals: {} + externals: () => { + return {}; + }, + externals2: {} }) && $.f)({}));" `); }); @@ -98,7 +107,8 @@ describe('[ROLLUP] tgpu alias gathering', () => { v: 1, name: undefined, ast: {"params":[],"body":[0,[]],"externalNames":{}}, - externals: { } + externals: () => ({}), + externals2: { } }) && $.f)({}))); " `); @@ -121,7 +131,8 @@ describe('[ROLLUP] tgpu alias gathering', () => { v: 1, name: undefined, ast: {"params":[],"body":[0,[]],"externalNames":{}}, - externals: { } + externals: () => ({}), + externals2: { } }) && $.f)({}))); " `); @@ -144,7 +155,8 @@ describe('[ROLLUP] tgpu alias gathering', () => { v: 1, name: undefined, ast: {"params":[],"body":[0,[]],"externalNames":{}}, - externals: { } + externals: () => ({}), + externals2: { } }) && $.f)({}))); " `); diff --git a/packages/unplugin-typegpu/test/auto-naming.test.ts b/packages/unplugin-typegpu/test/auto-naming.test.ts index 94b7fb600e..d62fe38b69 100644 --- a/packages/unplugin-typegpu/test/auto-naming.test.ts +++ b/packages/unplugin-typegpu/test/auto-naming.test.ts @@ -29,7 +29,10 @@ describe('[BABEL] auto naming', () => { body: [0, []], externalNames: {} }, - externals: {} + externals: () => { + return {}; + }, + externals2: {} }) && $.f)({})), "fn"); let shell = /*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.fn([]), "shell"); const cst = /*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.const(d.u32, 1), "cst"); @@ -133,7 +136,10 @@ describe('[BABEL] auto naming', () => { body: [0, [[10, [5, "0"]]]], externalNames: {} }, - externals: {} + externals: () => { + return {}; + }, + externals2: {} }) && $.f)({})), "myFunction"); const myComputeFn = /*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.computeFn({ workgroupSize: [1] @@ -145,7 +151,10 @@ describe('[BABEL] auto naming', () => { body: [0, []], externalNames: {} }, - externals: {} + externals: () => { + return {}; + }, + externals2: {} }) && $.f)({})), "myComputeFn"); const myVertexFn = /*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.vertexFn({ out: { @@ -163,7 +172,10 @@ describe('[BABEL] auto naming', () => { }]]]], externalNames: {} }, - externals: {} + externals: () => { + return {}; + }, + externals2: {} }) && $.f)({})), "myVertexFn"); const myFragmentFn = /*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.fragmentFn({ in: { @@ -182,7 +194,12 @@ describe('[BABEL] auto naming', () => { } } }, - externals: { + externals: () => { + return { + d + }; + }, + externals2: { d: { vec4f: () => d.vec4f } @@ -334,7 +351,10 @@ describe('[BABEL] auto naming', () => { body: [0, [[10, [5, "0"]]]], externalNames: {} }, - externals: {} + externals: () => { + return {}; + }, + externals2: {} }) && $.f)({}); const myFun1 = /*#__PURE__*/($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = () => { 'use gpu'; @@ -348,7 +368,10 @@ describe('[BABEL] auto naming', () => { body: [0, [[10, [5, "0"]]]], externalNames: {} }, - externals: {} + externals: () => { + return {}; + }, + externals2: {} }) && $.f)({}); const myFun2 = /*#__PURE__*/($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = function () { 'use gpu'; @@ -362,7 +385,10 @@ describe('[BABEL] auto naming', () => { body: [0, [[10, [5, "0"]]]], externalNames: {} }, - externals: {} + externals: () => { + return {}; + }, + externals2: {} }) && $.f)({});" `); }); @@ -510,7 +536,10 @@ describe('[BABEL] auto naming', () => { body: [0, []], externalNames: {} }, - externals: {} + externals: () => { + return {}; + }, + externals2: {} }) && $.f)({})), "myGuardedPipeline"); const anotherGuardedPipeline = /*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(root.createGuardedComputePipeline(/*#__PURE__*/($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = () => { 'use gpu'; @@ -522,7 +551,10 @@ describe('[BABEL] auto naming', () => { body: [0, []], externalNames: {} }, - externals: {} + externals: () => { + return {}; + }, + externals2: {} }) && $.f)({})).dispatchThreads(), "anotherGuardedPipeline"); console.log(myGuardedPipeline, anotherGuardedPipeline);" `); @@ -556,7 +588,8 @@ describe('[ROLLUP] auto naming', () => { v: 1, name: undefined, ast: {"params":[],"body":[0,[]],"externalNames":{}}, - externals: { } + externals: () => ({}), + externals2: { } }) && $.f)({}))), "fn")); (/*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.accessor(d.u32), "accessor")); (/*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.const(d.u32, 1), "cst")); @@ -640,14 +673,16 @@ describe('[ROLLUP] auto naming', () => { v: 1, name: undefined, ast: {"params":[],"body":[0,[[10,[5,"0"]]]],"externalNames":{}}, - externals: { } + externals: () => ({}), + externals2: { } }) && $.f)({}))), "myFunction")); (/*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.computeFn({ workgroupSize: [1] })( (/*#__PURE__*/($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => {}), { v: 1, name: undefined, ast: {"params":[],"body":[0,[]],"externalNames":{}}, - externals: { } + externals: () => ({}), + externals2: { } }) && $.f)({})), ), "myComputeFn")); (/*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.vertexFn({ out: { ret: d.i32 } })( @@ -655,7 +690,8 @@ describe('[ROLLUP] auto naming', () => { v: 1, name: undefined, ast: {"params":[],"body":[0,[[10,[104,{"ret":[5,"0"]}]]]],"externalNames":{}}, - externals: { } + externals: () => ({}), + externals2: { } }) && $.f)({})), ), "myVertexFn")); (/*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.fragmentFn({ @@ -666,7 +702,8 @@ describe('[ROLLUP] auto naming', () => { v: 1, name: undefined, ast: {"params":[],"body":[0,[[10,[6,[7,"d","vec4f"],[]]]]],"externalNames":{"d":{"vec4f":"d.vec4f"}}}, - externals: { d: { vec4f: () => d.vec4f } } + externals: () => ({d}), + externals2: { d: { vec4f: () => d.vec4f } } }) && $.f)({})), ), "myFragmentFn")); " @@ -821,7 +858,8 @@ describe('[ROLLUP] auto naming', () => { v: 1, name: "myFun3", ast: {"params":[],"body":[0,[[10,[5,"0"]]]],"externalNames":{}}, - externals: { } + externals: () => ({}), + externals2: { } }) && $.f)({})); const myFun1 = (/*#__PURE__*/($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => { @@ -831,7 +869,8 @@ describe('[ROLLUP] auto naming', () => { v: 1, name: "myFun1", ast: {"params":[],"body":[0,[[10,[5,"0"]]]],"externalNames":{}}, - externals: { } + externals: () => ({}), + externals2: { } }) && $.f)({})); const myFun2 = (/*#__PURE__*/($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (function () { @@ -841,7 +880,8 @@ describe('[ROLLUP] auto naming', () => { v: 1, name: "myFun2", ast: {"params":[],"body":[0,[[10,[5,"0"]]]],"externalNames":{}}, - externals: { } + externals: () => ({}), + externals2: { } }) && $.f)({})); @@ -1018,7 +1058,8 @@ describe('[ROLLUP] auto naming', () => { v: 1, name: undefined, ast: {"params":[],"body":[0,[]],"externalNames":{}}, - externals: { } + externals: () => ({}), + externals2: { } }) && $.f)({}))), "myGuardedPipeline")); const anotherGuardedPipeline = (/*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(root @@ -1028,7 +1069,8 @@ describe('[ROLLUP] auto naming', () => { v: 1, name: undefined, ast: {"params":[],"body":[0,[]],"externalNames":{}}, - externals: { } + externals: () => ({}), + externals2: { } }) && $.f)({}))) .dispatchThreads(), "anotherGuardedPipeline")); diff --git a/packages/unplugin-typegpu/test/nested-externals.test.ts b/packages/unplugin-typegpu/test/nested-externals.test.ts index 9a3d3acf53..0c32a25af9 100644 --- a/packages/unplugin-typegpu/test/nested-externals.test.ts +++ b/packages/unplugin-typegpu/test/nested-externals.test.ts @@ -79,7 +79,12 @@ describe('externals gathering', () => { const code = codes['allows multiple usages of one external']; expect(extractExternals(babelTransform(code))).toMatchInlineSnapshot(` - "{ + "() => { + return { + ext + }; + }, + externals2: { ext: { value: () => ext.value, config: { @@ -95,7 +100,12 @@ describe('externals gathering', () => { const code = codes['treats dereference like a regular external']; expect(extractExternals(babelTransform(code))).toMatchInlineSnapshot(` - "{ + "() => { + return { + buffer + }; + }, + externals2: { buffer: { $: { x: () => buffer.$.x @@ -109,7 +119,12 @@ describe('externals gathering', () => { const code = codes['skips computed prop access']; expect(extractExternals(babelTransform(code))).toMatchInlineSnapshot(` - "{ + "() => { + return { + ext + }; + }, + externals2: { ext: () => ext }" `); @@ -119,7 +134,12 @@ describe('externals gathering', () => { const code = codes['skips calls']; expect(extractExternals(babelTransform(code))).toMatchInlineSnapshot(` - "{ + "() => { + return { + ext + }; + }, + externals2: { ext: { comptime: () => ext.comptime, runtime: () => ext.runtime @@ -134,7 +154,10 @@ describe('externals gathering', () => { const code = codes['allows multiple usages of one external']; expect(extractExternals(await rollupTransform(code))).toMatchInlineSnapshot( - `"{ ext: { value: () => ext.value, config: { multiplier: () => ext.config.multiplier, zero: () => ext.config.zero } } }"`, + ` + "() => ({ext}), + externals2: { ext: { value: () => ext.value, config: { multiplier: () => ext.config.multiplier, zero: () => ext.config.zero } } }" + `, ); }); @@ -142,7 +165,10 @@ describe('externals gathering', () => { const code = codes['treats dereference like a regular external']; expect(extractExternals(await rollupTransform(code))).toMatchInlineSnapshot( - `"{ buffer: { $: { x: () => buffer.$.x } } }"`, + ` + "() => ({buffer}), + externals2: { buffer: { $: { x: () => buffer.$.x } } }" + `, ); }); @@ -150,7 +176,10 @@ describe('externals gathering', () => { const code = codes['skips computed prop access']; expect(extractExternals(await rollupTransform(code))).toMatchInlineSnapshot( - `"{ ext: () => ext }"`, + ` + "() => ({ext}), + externals2: { ext: () => ext }" + `, ); }); @@ -158,7 +187,10 @@ describe('externals gathering', () => { const code = codes['skips calls']; expect(extractExternals(await rollupTransform(code))).toMatchInlineSnapshot( - `"{ ext: { comptime: () => ext.comptime, runtime: () => ext.runtime } }"`, + ` + "() => ({ext}), + externals2: { ext: { comptime: () => ext.comptime, runtime: () => ext.runtime } }" + `, ); }); }); diff --git a/packages/unplugin-typegpu/test/parser-options.test.ts b/packages/unplugin-typegpu/test/parser-options.test.ts index 444df9d924..6cd850ff8f 100644 --- a/packages/unplugin-typegpu/test/parser-options.test.ts +++ b/packages/unplugin-typegpu/test/parser-options.test.ts @@ -23,7 +23,10 @@ describe('[BABEL] parser options', () => { body: [0, [[13, "x", [1, [5, "2"], "+", [5, "2"]]]]], externalNames: {} }, - externals: {} + externals: () => { + return {}; + }, + externals2: {} }) && $.f)({}));" `); @@ -60,7 +63,8 @@ describe('[ROLLUP] tgpu alias gathering', async () => { v: 1, name: undefined, ast: {"params":[],"body":[0,[]],"externalNames":{}}, - externals: { } + externals: () => ({}), + externals2: { } }) && $.f)({}))); console.log(increment); diff --git a/packages/unplugin-typegpu/test/tgsl-transpiling.test.ts b/packages/unplugin-typegpu/test/tgsl-transpiling.test.ts index 4ee152b384..deab2cb936 100644 --- a/packages/unplugin-typegpu/test/tgsl-transpiling.test.ts +++ b/packages/unplugin-typegpu/test/tgsl-transpiling.test.ts @@ -58,7 +58,13 @@ describe('[BABEL] plugin for transpiling tgsl functions to tinyest', () => { } } }, - externals: { + externals: () => { + return { + counter, + d + }; + }, + externals2: { counter: { $: { x: () => counter.$.x, @@ -109,7 +115,10 @@ describe('[BABEL] plugin for transpiling tgsl functions to tinyest', () => { body: [0, [[13, "x", true]]], externalNames: {} }, - externals: {} + externals: () => { + return {}; + }, + externals2: {} }) && $.f)({})); const b = tgpu.fn([])(/*#__PURE__*/($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = () => { const y = 2 + 2; @@ -121,7 +130,10 @@ describe('[BABEL] plugin for transpiling tgsl functions to tinyest', () => { body: [0, [[13, "y", [1, [5, "2"], "+", [5, "2"]]]]], externalNames: {} }, - externals: {} + externals: () => { + return {}; + }, + externals2: {} }) && $.f)({})); const cx = 2; const c = tgpu.fn([])(/*#__PURE__*/($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = () => cx, { @@ -134,7 +146,12 @@ describe('[BABEL] plugin for transpiling tgsl functions to tinyest', () => { cx: "cx" } }, - externals: { + externals: () => { + return { + cx + }; + }, + externals2: { cx: () => cx } }) && $.f)({})); @@ -191,7 +208,10 @@ describe('[BABEL] plugin for transpiling tgsl functions to tinyest', () => { body: [0, [[13, "x", true]]], externalNames: {} }, - externals: {} + externals: () => { + return {}; + }, + externals2: {} }) && $.f)({})); const funcWithAs = tgpu.computeFn({ workgroupSize: [1] @@ -208,7 +228,10 @@ describe('[BABEL] plugin for transpiling tgsl functions to tinyest', () => { body: [0, [[13, "x", true]]], externalNames: {} }, - externals: {} + externals: () => { + return {}; + }, + externals2: {} }) && $.f)({})); const funcWithSatisfies = tgpu.computeFn({ workgroupSize: [1] @@ -225,7 +248,10 @@ describe('[BABEL] plugin for transpiling tgsl functions to tinyest', () => { body: [0, [[13, "x", true]]], externalNames: {} }, - externals: {} + externals: () => { + return {}; + }, + externals2: {} }) && $.f)({}));" `); }); @@ -270,7 +296,12 @@ describe('[BABEL] plugin for transpiling tgsl functions to tinyest', () => { } } }, - externals: { + externals: () => { + return { + this: this + }; + }, + externals2: { this: { myBuffer: { $: () => this.myBuffer.$ @@ -324,7 +355,8 @@ describe('[ROLLUP] plugin for transpiling tgsl functions to tinyest', () => { v: 1, name: undefined, ast: {"params":[{"type":"i","name":"input"}],"body":[0,[[13,"tmp",[7,[7,"counter","$"],"x"]],[2,[7,[7,"counter","$"],"x"],"=",[7,[7,"counter","$"],"y"]],[2,[7,[7,"counter","$"],"y"],"+=","tmp"],[2,[7,[7,"counter","$"],"z"],"+=",[6,[7,"d","f32"],[[7,[7,"input","num"],"x"]]]]]],"externalNames":{"counter":{"$":{"x":"counter.$.x","y":"counter.$.y","z":"counter.$.z"}},"d":{"f32":"d.f32"}}}, - externals: { counter: { $: { x: () => counter.$.x, y: () => counter.$.y, z: () => counter.$.z } }, d: { f32: () => d.f32 } } + externals: () => ({counter, d}), + externals2: { counter: { $: { x: () => counter.$.x, y: () => counter.$.y, z: () => counter.$.z } }, d: { f32: () => d.f32 } } }) && $.f)({}))); " `); @@ -356,7 +388,8 @@ describe('[ROLLUP] plugin for transpiling tgsl functions to tinyest', () => { v: 1, name: undefined, ast: {"params":[{"type":"i","name":"input"}],"body":[0,[[13,"x",true]]],"externalNames":{}}, - externals: { } + externals: () => ({}), + externals2: { } }) && $.f)({}))); tgpu.fn([])((/*#__PURE__*/($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => { @@ -364,7 +397,8 @@ describe('[ROLLUP] plugin for transpiling tgsl functions to tinyest', () => { v: 1, name: undefined, ast: {"params":[],"body":[0,[[13,"y",[1,[5,"2"],"+",[5,"2"]]]]],"externalNames":{}}, - externals: { } + externals: () => ({}), + externals2: { } }) && $.f)({}))); const cx = 2; @@ -372,7 +406,8 @@ describe('[ROLLUP] plugin for transpiling tgsl functions to tinyest', () => { v: 1, name: undefined, ast: {"params":[],"body":[0,[[10,"cx"]]],"externalNames":{"cx":"cx"}}, - externals: { cx: () => cx } + externals: () => ({cx}), + externals2: { cx: () => cx } }) && $.f)({}))); tgpu.fn([])('() {}'); @@ -429,7 +464,8 @@ describe('[ROLLUP] plugin for transpiling tgsl functions to tinyest', () => { v: 1, name: undefined, ast: {"params":[],"body":[0,[[10,[7,[7,"this","myBuffer"],"$"]]]],"externalNames":{"this":{"myBuffer":{"$":"this.myBuffer.$"}}}}, - externals: { this: { myBuffer: { $: () => this.myBuffer.$ } } } + externals: () => ({"this": this}), + externals2: { this: { myBuffer: { $: () => this.myBuffer.$ } } } }) && $.f)({}))); } diff --git a/packages/unplugin-typegpu/test/typescript-syntax.test.ts b/packages/unplugin-typegpu/test/typescript-syntax.test.ts index 44dd8f6a77..f0a5d475dc 100644 --- a/packages/unplugin-typegpu/test/typescript-syntax.test.ts +++ b/packages/unplugin-typegpu/test/typescript-syntax.test.ts @@ -31,7 +31,10 @@ describe('as type', () => { body: [0, [[10, [1, "a", "+", "b"]]]], externalNames: {} }, - externals: {} + externals: () => { + return {}; + }, + externals2: {} }) && $.f)({});" `); }); diff --git a/packages/unplugin-typegpu/test/use-gpu-directive.test.ts b/packages/unplugin-typegpu/test/use-gpu-directive.test.ts index 1c18711ac9..3db2c75e36 100644 --- a/packages/unplugin-typegpu/test/use-gpu-directive.test.ts +++ b/packages/unplugin-typegpu/test/use-gpu-directive.test.ts @@ -39,7 +39,10 @@ describe('"use gpu" marked arrow function, assigned to a const', () => { body: [0, [[10, [1, "a", "+", "b"]]]], externalNames: {} }, - externals: {} + externals: () => { + return {}; + }, + externals2: {} }) && $.f)({}); const addCPU = (a, b) => { return a + b; @@ -59,7 +62,8 @@ describe('"use gpu" marked arrow function, assigned to a const', () => { v: 1, name: "addGPU", ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":{}}, - externals: { } + externals: () => ({}), + externals2: { } }) && $.f)({})); const addCPU = (a, b) => { @@ -110,7 +114,10 @@ describe('marked arrow functions passed to shells', () => { body: [0, [[10, [1, "a", "+", "b"]]]], externalNames: {} }, - externals: {} + externals: () => { + return {}; + }, + externals2: {} }) && $.f)({})); shell((a, b) => { return a + b; @@ -131,7 +138,8 @@ describe('marked arrow functions passed to shells', () => { v: 1, name: undefined, ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":{}}, - externals: { } + externals: () => ({}), + externals2: { } }) && $.f)({}))); shell((a, b) => { @@ -180,7 +188,10 @@ describe('marked anonymous function expressions passed to shells', () => { body: [0, [[10, [1, "a", "+", "b"]]]], externalNames: {} }, - externals: {} + externals: () => { + return {}; + }, + externals2: {} }) && $.f)({})); shell(function (a, b) { return a + b; @@ -201,7 +212,8 @@ describe('marked anonymous function expressions passed to shells', () => { v: 1, name: undefined, ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":{}}, - externals: { } + externals: () => ({}), + externals2: { } }) && $.f)({}))); shell(function(a, b) { @@ -250,7 +262,10 @@ describe('marked named function expressions passed to shells', () => { body: [0, [[10, [1, "a", "+", "b"]]]], externalNames: {} }, - externals: {} + externals: () => { + return {}; + }, + externals2: {} }) && $.f)({})); shell(function addCPU(a, b) { return a + b; @@ -271,7 +286,8 @@ describe('marked named function expressions passed to shells', () => { v: 1, name: "addGPU", ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":{}}, - externals: { } + externals: () => ({}), + externals2: { } }) && $.f)({}))); shell(function addCPU(a, b) { @@ -321,7 +337,10 @@ describe('marked function statements', () => { body: [0, [[10, [1, "a", "+", "b"]]]], externalNames: {} }, - externals: {} + externals: () => { + return {}; + }, + externals2: {} }) && $.f)({}); function addCPU(a, b) { return a + b; @@ -341,7 +360,8 @@ describe('marked function statements', () => { v: 1, name: "addGPU", ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":{}}, - externals: { } + externals: () => ({}), + externals2: { } }) && $.f)({})); @@ -407,7 +427,10 @@ describe('marked object methods', () => { body: [0, [[10, [1, "a", "%", "b"]]]], externalNames: {} }, - externals: {} + externals: () => { + return {}; + }, + externals2: {} }) && $.f)({}) }; @@ -439,7 +462,12 @@ describe('marked object methods', () => { } } }, - externals: { + externals: () => { + return { + obj + }; + }, + externals2: { obj: { mod: () => obj.mod } @@ -460,7 +488,8 @@ describe('marked object methods', () => { v: 1, name: "mod", ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","%","b"]]]],"externalNames":{}}, - externals: { } + externals: () => ({}), + externals2: { } }) && $.f)({})) }; @@ -481,7 +510,8 @@ describe('marked object methods', () => { v: 1, name: "isPrime", ast: {"params":[{"type":"i","name":"n"}],"body":[0,[[11,[1,"n","<=",[5,"1"]],[0,[[10,false]]]],[14,[12,"i",[5,"2"]],[1,"i","<","n"],[102,"++","i"],[0,[[11,[1,[6,[7,"obj","mod"],["n","i"]],"===",[5,"0"]],[0,[[10,false]]]]]]],[10,true]]],"externalNames":{"obj":{"mod":"obj.mod"}}}, - externals: { obj: { mod: () => obj.mod } } + externals: () => ({obj}), + externals2: { obj: { mod: () => obj.mod } } }) && $.f)({})); console.log(obj, isPrime); @@ -541,7 +571,12 @@ describe('transforms numeric operations', () => { } } }, - externals: { + externals: () => { + return { + countMutable + }; + }, + externals2: { countMutable: { $: () => countMutable.$ } @@ -569,7 +604,8 @@ describe('transforms numeric operations', () => { v: 1, name: "main", ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[12,"c",[1,[1,"a","+","b"],"+",[5,"2"]]],[2,"c","+=",[1,[5,"2"],"*","b"]],[2,[7,"countMutable","$"],"+=",[5,"3"]]]],"externalNames":{"countMutable":{"$":"countMutable.$"}}}, - externals: { countMutable: { $: () => countMutable.$ } } + externals: () => ({countMutable}), + externals2: { countMutable: { $: () => countMutable.$ } } }) && $.f)({})); console.log(main); @@ -620,7 +656,10 @@ describe('hoists global function statements marked with "use gpu"', () => { body: [0, [[10, [1, "a", "*", "b"]]]], externalNames: {} }, - externals: {} + externals: () => { + return {}; + }, + externals2: {} }) && $.f)({}); /** ADD */ // another comment @@ -642,7 +681,10 @@ describe('hoists global function statements marked with "use gpu"', () => { body: [0, [[10, [1, "a", "+", "b"]]]], externalNames: {} }, - externals: {} + externals: () => { + return {}; + }, + externals2: {} }) && $.f)({}); console.log(add, mul);" `); @@ -659,7 +701,8 @@ describe('hoists global function statements marked with "use gpu"', () => { v: 1, name: "mul", ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","*","b"]]]],"externalNames":{}}, - externals: { } + externals: () => ({}), + externals2: { } }) && $.f)({})); /** ADD */ @@ -671,7 +714,8 @@ describe('hoists global function statements marked with "use gpu"', () => { v: 1, name: "add", ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":{}}, - externals: { } + externals: () => ({}), + externals2: { } }) && $.f)({})); console.log(add, mul); @@ -724,7 +768,10 @@ describe('hoists function statements marked with "use gpu", scoped inside anothe body: [0, [[10, [1, "a", "*", "b"]]]], externalNames: {} }, - externals: {} + externals: () => { + return {}; + }, + externals2: {} }) && $.f)({}); /** ADD */ // another comment @@ -746,7 +793,10 @@ describe('hoists function statements marked with "use gpu", scoped inside anothe body: [0, [[10, [1, "a", "+", "b"]]]], externalNames: {} }, - externals: {} + externals: () => { + return {}; + }, + externals2: {} }) && $.f)({}); console.log(add, mul); }" @@ -765,7 +815,8 @@ describe('hoists function statements marked with "use gpu", scoped inside anothe v: 1, name: "mul", ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","*","b"]]]],"externalNames":{}}, - externals: { } + externals: () => ({}), + externals2: { } }) && $.f)({})); /** ADD */ @@ -777,7 +828,8 @@ describe('hoists function statements marked with "use gpu", scoped inside anothe v: 1, name: "add", ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":{}}, - externals: { } + externals: () => ({}), + externals2: { } }) && $.f)({})); console.log(add, mul); @@ -841,7 +893,10 @@ describe('hoists function statements marked with "use gpu", scoped inside an arr body: [0, [[10, [1, "a", "*", "b"]]]], externalNames: {} }, - externals: {} + externals: () => { + return {}; + }, + externals2: {} }) && $.f)({}); /** ADD */ // another comment @@ -863,7 +918,10 @@ describe('hoists function statements marked with "use gpu", scoped inside an arr body: [0, [[10, [1, "a", "+", "b"]]]], externalNames: {} }, - externals: {} + externals: () => { + return {}; + }, + externals2: {} }) && $.f)({}); console.log(add, mul); };" @@ -882,7 +940,8 @@ describe('hoists function statements marked with "use gpu", scoped inside an arr v: 1, name: "mul", ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","*","b"]]]],"externalNames":{}}, - externals: { } + externals: () => ({}), + externals2: { } }) && $.f)({})); /** ADD */ @@ -894,7 +953,8 @@ describe('hoists function statements marked with "use gpu", scoped inside an arr v: 1, name: "add", ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":{}}, - externals: { } + externals: () => ({}), + externals2: { } }) && $.f)({})); console.log(add, mul); @@ -962,7 +1022,12 @@ describe('hoists function statements marked with "use gpu", scoped inside an if c: "c" } }, - externals: { + externals: () => { + return { + c + }; + }, + externals2: { c: () => c } }) && $.f)({}); @@ -988,7 +1053,12 @@ describe('hoists function statements marked with "use gpu", scoped inside an if c: "c" } }, - externals: { + externals: () => { + return { + c + }; + }, + externals2: { c: () => c } }) && $.f)({}); @@ -1010,7 +1080,8 @@ describe('hoists function statements marked with "use gpu", scoped inside an if v: 1, name: "mul", ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,[1,"a","*","b"],"*","c"]]]],"externalNames":{"c":"c"}}, - externals: { c: () => c } + externals: () => ({c}), + externals2: { c: () => c } }) && $.f)({})); /** ADD */ @@ -1022,7 +1093,8 @@ describe('hoists function statements marked with "use gpu", scoped inside an if v: 1, name: "add", ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,[1,"a","+","b"],"+","c"]]]],"externalNames":{"c":"c"}}, - externals: { c: () => c } + externals: () => ({c}), + externals2: { c: () => c } }) && $.f)({})); console.log(add, mul); @@ -1094,7 +1166,12 @@ describe('replaces function statements marked with "use gpu" in place when condi c: "c" } }, - externals: { + externals: () => { + return { + c + }; + }, + externals2: { c: () => c } }) && $.f)({}); @@ -1122,7 +1199,12 @@ describe('replaces function statements marked with "use gpu" in place when condi c: "c" } }, - externals: { + externals: () => { + return { + c + }; + }, + externals2: { c: () => c } }) && $.f)({}); @@ -1147,7 +1229,8 @@ describe('replaces function statements marked with "use gpu" in place when condi v: 1, name: "add", ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,[1,"a","+","b"],"+","c"]]]],"externalNames":{"c":"c"}}, - externals: { c: () => c } + externals: () => ({c}), + externals2: { c: () => c } }) && $.f)({})); @@ -1162,7 +1245,8 @@ describe('replaces function statements marked with "use gpu" in place when condi v: 1, name: "mul", ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,[1,"a","*","b"],"*","c"]]]],"externalNames":{"c":"c"}}, - externals: { c: () => c } + externals: () => ({c}), + externals2: { c: () => c } }) && $.f)({})); @@ -1201,7 +1285,8 @@ test('hoists exported marked function statements', async () => { v: 1, name: "mul", ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","*","b"]]]],"externalNames":{}}, - externals: { } + externals: () => ({}), + externals2: { } }) && $.f)({})); /** ADD */ @@ -1212,7 +1297,8 @@ test('hoists exported marked function statements', async () => { v: 1, name: "add", ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":{}}, - externals: { } + externals: () => ({}), + externals2: { } }) && $.f)({})); console.log(add); From e29acd44f53b49022c6168e342b064d129cec47a Mon Sep 17 00:00:00 2001 From: Aleksander Katan <56294622+aleksanderkatan@users.noreply.github.com> Date: Wed, 29 Apr 2026 17:35:37 +0200 Subject: [PATCH 12/12] Handle externals2 if present --- packages/typegpu/src/core/function/fnCore.ts | 29 ++++++++++++++++---- packages/typegpu/src/shared/meta.ts | 5 ++++ 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/packages/typegpu/src/core/function/fnCore.ts b/packages/typegpu/src/core/function/fnCore.ts index 3d9cd41f81..8f6262eedc 100644 --- a/packages/typegpu/src/core/function/fnCore.ts +++ b/packages/typegpu/src/core/function/fnCore.ts @@ -3,7 +3,7 @@ import { undecorate } from '../../data/dataTypes.ts'; import { type ResolvedSnippet, snip } from '../../data/snippet.ts'; import { type BaseData, isWgslData, isWgslStruct, Void } from '../../data/wgslTypes.ts'; import { MissingLinksError } from '../../errors.ts'; -import { getMetaData, getName } from '../../shared/meta.ts'; +import { getMetaData, getName, type Externals2 } from '../../shared/meta.ts'; import { $getNameForward } from '../../shared/symbols.ts'; import type { ResolutionCtx } from '../../types.ts'; import { applyExternals, type ExternalMap, replaceExternalsInWgsl } from '../resolve/externals.ts'; @@ -148,11 +148,14 @@ export function createFnCore(implementation: Implementation, fnAttribute = ''): const pluginData = getMetaData(implementation); // Passing a record happens prior to version 0.9.0 + // Passing a function happens prior to version 0.12.0 // TODO: Support for this can be removed down the line - const pluginExternals = - typeof pluginData?.externals === 'function' - ? pluginData.externals() - : pluginData?.externals; + let pluginExternals: ExternalMap | Record | undefined = + pluginData?.externals2 + ? externals2ToExternalMap(pluginData.externals2) + : typeof pluginData?.externals === 'function' + ? pluginData.externals() + : pluginData?.externals; if (pluginExternals) { const missing = Object.fromEntries( @@ -220,6 +223,22 @@ export function createFnCore(implementation: Implementation, fnAttribute = ''): return core; } +// TODO: deslopify, document, make sure it works as intended +function externals2ToExternalMap(ext2: Externals2): ExternalMap { + const result: ExternalMap = {}; + for (const [key, value] of Object.entries(ext2)) { + if (typeof value === 'function') { + Object.defineProperty(result, key, { + get: value, + enumerable: true, + }); + } else { + result[key] = externals2ToExternalMap(value); + } + } + return result; +} + function isArgUsedInBody(argName: string, body: string): boolean { return new RegExp(`\\b${argName}\\b`).test(body); } diff --git a/packages/typegpu/src/shared/meta.ts b/packages/typegpu/src/shared/meta.ts index 031c95895a..b2d2eae87e 100644 --- a/packages/typegpu/src/shared/meta.ts +++ b/packages/typegpu/src/shared/meta.ts @@ -8,6 +8,10 @@ export interface Externals { [key: string]: Externals | string; } +export interface Externals2 { + [key: string]: Externals2 | (() => unknown); +} + export interface MetaData { v?: number; name?: string | undefined; @@ -22,6 +26,7 @@ export interface MetaData { // Passing a record happens prior to version 0.9.0 // TODO: Support for this can be removed down the line Record | (() => Record) | undefined; + externals2?: Externals2; } /**