Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
116 commits
Select commit Hold shift + click to select a range
466c536
Add tests stub
aleksanderkatan Apr 28, 2026
4c88b76
Add more babel tests
aleksanderkatan Apr 28, 2026
7649af2
Update babel tests
aleksanderkatan Apr 28, 2026
6f47035
Add rollup tests
aleksanderkatan Apr 28, 2026
9605b2d
Keep ancestor chain in context in tinyest-for-wgsl
aleksanderkatan Apr 28, 2026
f4e1bab
Change externals from array to object
aleksanderkatan Apr 29, 2026
49e7596
Add addExternal function
aleksanderkatan Apr 29, 2026
beca358
Add a rudimentary implementation of external collection
aleksanderkatan Apr 29, 2026
4257b1d
Update bahbel externals generation
aleksanderkatan Apr 29, 2026
d6e4f7e
Update rollup
aleksanderkatan Apr 29, 2026
f11b111
Bump metadata version
aleksanderkatan May 28, 2026
9efa70c
Update remaining tests
aleksanderkatan May 28, 2026
5726bea
Split parsers.ts into three files
aleksanderkatan May 29, 2026
b6b2458
Clean up external addition in parsers.ts
aleksanderkatan May 29, 2026
57232ce
Add sanity tests for external prop access
aleksanderkatan May 29, 2026
d0db0fd
Revert externalNames to an array
aleksanderkatan May 29, 2026
6aeefe0
More tests
aleksanderkatan May 29, 2026
41276f7
Deduplicate Externals interface
aleksanderkatan Jun 8, 2026
cbc50bc
nits
aleksanderkatan Jun 8, 2026
c124e93
Merge remote-tracking branch 'origin/main' into feat/expand-property-…
aleksanderkatan Jun 8, 2026
c90f042
Review fixes
aleksanderkatan Jun 8, 2026
9bc7945
Wrap prop names in ""
aleksanderkatan Jun 8, 2026
2427b87
Add private prop autonaming tests
aleksanderkatan Jun 8, 2026
25b87a6
Autoname private properties
aleksanderkatan Jun 8, 2026
cc4e34e
Handle private property access
aleksanderkatan Jun 9, 2026
203e37c
Wrap prop names in "" in babel
aleksanderkatan Jun 9, 2026
d6808ca
Only wrap when necessary
aleksanderkatan Jun 9, 2026
1c36feb
Update tests
aleksanderkatan Jun 9, 2026
a1d7d12
Stop linting private property
aleksanderkatan Jun 9, 2026
c3b2b63
Review fix
aleksanderkatan Jun 9, 2026
d3bdf17
Merge branch 'main' into feat/expand-property-access-in-externals
aleksanderkatan Jun 9, 2026
5f578ef
Update parser
aleksanderkatan Jun 9, 2026
23b4f44
Collect externals from top in parsers and replace them in tinyest
aleksanderkatan Jun 10, 2026
8877ba1
Update unplugin and parsers tests
aleksanderkatan Jun 10, 2026
66a5090
broken test
aleksanderkatan Jun 12, 2026
669e47f
Remove setName from mergeExternals
aleksanderkatan Jun 16, 2026
692ac90
Name unnamed externals in propAccess and getById
aleksanderkatan Jun 16, 2026
a944c60
Limit tgpuDeclare to one external
aleksanderkatan Jun 16, 2026
754eb92
Remove unnecessary merge from tgpuResolve
aleksanderkatan Jun 16, 2026
a217940
Limit rawCodeSnippet to one $uses
aleksanderkatan Jun 16, 2026
1f8bccb
Add FnExternals, and throw if $uses is called multiple times
aleksanderkatan Jun 16, 2026
b0ec447
Prepare string-implemented functions for external swap
aleksanderkatan Jun 16, 2026
236075c
Finish replacing old externals with new externals && cleanup
aleksanderkatan Jun 19, 2026
7c7faad
More cleanup
aleksanderkatan Jun 19, 2026
a03b83f
Detect $uses on function with plugin metadata earlier
aleksanderkatan Jun 19, 2026
1a16a8b
Add FnExternals docs
aleksanderkatan Jun 19, 2026
9f33691
Add external name collision tests
aleksanderkatan Jun 19, 2026
052f36b
Add more tests & fix fragment out external
aleksanderkatan Jun 19, 2026
5ec7052
More tests && allow redundant external
aleksanderkatan Jun 19, 2026
54b8b27
Update docs
aleksanderkatan Jun 19, 2026
d7d48ae
Add compute tests
aleksanderkatan Jun 22, 2026
fc8b147
Move external tests to external.test.ts
aleksanderkatan Jun 22, 2026
cfb074a
Fix old external tests by tweaking the api
aleksanderkatan Jun 22, 2026
5b26ee6
nr fix
aleksanderkatan Jun 22, 2026
312f78d
Merge branch 'main' into fix/forbid-multiple-uses
aleksanderkatan Jun 22, 2026
29b0354
self-review fixes
aleksanderkatan Jun 22, 2026
bd481a7
Merge branch 'main' into feat/expand-property-access-in-externals
aleksanderkatan Jun 22, 2026
a06100f
Merge branch 'feat/flatten-externals' into feat/expand-property-acces…
aleksanderkatan Jun 22, 2026
638fe50
Update tests
aleksanderkatan Jun 22, 2026
56534b3
Simplify normalizeMetadataV2, update tests
aleksanderkatan Jun 22, 2026
5c7e23c
Simplify externals usage in plugins
aleksanderkatan Jun 22, 2026
59adaba
Optimize and document `tryFindExternalChain`
aleksanderkatan Jun 22, 2026
b84d942
Remove ancestor chain
aleksanderkatan Jun 22, 2026
2f38b37
Update tests
aleksanderkatan Jun 22, 2026
a3c5ad3
Update jsdoc
aleksanderkatan Jun 22, 2026
f9a4385
Fix multiple .$uses error
aleksanderkatan Jun 22, 2026
5d3386d
Allow externals to be undefined
aleksanderkatan Jun 22, 2026
83ee5b0
Fix external naming in wgslGenerator
aleksanderkatan Jun 22, 2026
7aaefbc
Merge branch 'fix/forbid-multiple-uses' into feat/expand-property-acc…
aleksanderkatan Jun 22, 2026
f8d8ed4
Update snapshots
aleksanderkatan Jun 22, 2026
c2a3141
Add a nested unnamed struct external test
aleksanderkatan Jun 26, 2026
d63d3bf
Merge remote-tracking branch 'origin/main' into feat/expand-property-…
aleksanderkatan Jun 26, 2026
ce0b389
Add some explaining the magic docs
aleksanderkatan Jun 26, 2026
45a3241
Add remaining docs
aleksanderkatan Jun 26, 2026
c6a3d52
Cut off flattening at '$'
aleksanderkatan Jun 26, 2026
2f75602
Update snapshots
aleksanderkatan Jun 26, 2026
d35ea36
Change externals array to set
aleksanderkatan Jun 26, 2026
37ed53e
Revert external undefined changes
aleksanderkatan Jun 26, 2026
953623e
Update external tests
aleksanderkatan Jun 26, 2026
1cd75cf
Review and update the doc
aleksanderkatan Jun 26, 2026
f30d344
Update snapshot
aleksanderkatan Jun 26, 2026
0a6b605
Update docs and tests
aleksanderkatan Jun 29, 2026
a9cb1f9
Review fixes
aleksanderkatan Jul 2, 2026
7fb2bb2
Merge remote-tracking branch 'origin/main' into feat/expand-property-…
aleksanderkatan Jul 2, 2026
2e94481
Self review
aleksanderkatan Jul 2, 2026
14ef463
Merge branch 'main' into feat/expand-property-access-in-externals
aleksanderkatan Jul 2, 2026
af80bcf
Apply suggestions from code review
aleksanderkatan Jul 6, 2026
5775d25
Remove externalNames
aleksanderkatan Jul 6, 2026
60d3a9c
Update snapshots
aleksanderkatan Jul 6, 2026
5ef3624
Remove redundant docs from shader-generation
aleksanderkatan Jul 6, 2026
1cfd5c4
Merge branch 'main' into feat/expand-property-access-in-externals
aleksanderkatan Jul 6, 2026
9628fa0
Move visited set onto context
aleksanderkatan Jul 7, 2026
6f0c43e
Merge branch 'main' into feat/expand-property-access-in-externals
aleksanderkatan Jul 7, 2026
18a30f4
Merge branch 'feat/expand-property-access-in-externals' into feat/pri…
aleksanderkatan Jul 7, 2026
1bfb85c
Merge fixes
aleksanderkatan Jul 7, 2026
9520237
Remove unreachable nodes
aleksanderkatan Jul 7, 2026
f09b819
Remove unused function
aleksanderkatan Jul 7, 2026
a5e96bc
Merge branch 'main' into feat/expand-property-access-in-externals
aleksanderkatan Jul 8, 2026
648dc26
Include the entire chain in autonamed externals
aleksanderkatan Jul 8, 2026
f492202
Apply suggestions from code review
aleksanderkatan Jul 8, 2026
58525a5
Merge branch 'main' into feat/expand-property-access-in-externals
aleksanderkatan Jul 8, 2026
f7b9498
Update packages/tinyest-for-wgsl/src/parsers.ts
aleksanderkatan Jul 9, 2026
191f7a0
Update apps/typegpu-docs/src/content/docs/advanced/explaining-the-mag…
aleksanderkatan Jul 9, 2026
1f3fc01
Kill spaces
aleksanderkatan Jul 9, 2026
7f22e3a
Update typegpu snapshots
aleksanderkatan Jul 9, 2026
dba4159
Only cache visited member expressions
aleksanderkatan Jul 9, 2026
defe487
Merge remote-tracking branch 'origin/main' into feat/expand-property-…
aleksanderkatan Jul 10, 2026
d743d93
Merge fixes
aleksanderkatan Jul 10, 2026
886759b
Merge remote-tracking branch 'origin/feat/expand-property-access-in-e…
aleksanderkatan Jul 10, 2026
4acec49
Review fix
aleksanderkatan Jul 10, 2026
4be9449
nr fix
aleksanderkatan Jul 10, 2026
afbddef
Merge remote-tracking branch 'origin/main' into feat/private-property…
aleksanderkatan Jul 13, 2026
15b6ec6
Merge fixes
aleksanderkatan Jul 13, 2026
a1c4c89
Merge branch 'main' into feat/private-property-access
aleksanderkatan Jul 13, 2026
bcd2ff0
Merge branch 'main' into feat/private-property-access
aleksanderkatan Jul 14, 2026
3920ff6
Merge branch 'main' into feat/private-property-access
aleksanderkatan Jul 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions packages/eslint-plugin/src/rules/noUnsupportedSyntax.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,13 +140,6 @@ export const noUnsupportedSyntax = createRule({
report(node, `'new' expression`);
},

PrivateIdentifier(node) {
if (!directives.getEnclosingTypegpuFunction()) {
return;
}
report(node, 'private identifier');
},

Property(node) {
if (!directives.getEnclosingTypegpuFunction()) {
return;
Expand Down
10 changes: 1 addition & 9 deletions packages/eslint-plugin/tests/rules/noUnsupportedSyntax.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ describe('noUnsupportedSyntax', () => {
"const fn = () => { 'use gpu'; const x = 1; }",
"const fn = () => { 'use gpu'; const x = Struct({ prop: 1}); }",
"const fn = () => { 'use gpu'; let x = 1; }",
"const cls = new (class { #priv = 1; fn = () => { 'use gpu'; const a = this.#priv; } } )()",
],
Comment thread
aleksanderkatan marked this conversation as resolved.
invalid: [
{
Expand Down Expand Up @@ -209,15 +210,6 @@ describe('noUnsupportedSyntax', () => {
},
],
},
{
code: "const fn = () => { 'use gpu'; obj.#buffer.$ = 1; }",
errors: [
{
messageId: 'unexpected',
data: { snippet: '#buffer', syntax: 'private identifier' },
},
],
},
{
code: "const fn = () => { 'use gpu'; const obj = { [key]: 1 }; }",
errors: [
Expand Down
14 changes: 11 additions & 3 deletions packages/tinyest-for-wgsl/src/externals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,26 @@ export function tryFindExternalChain(ctx: Context, node: JsNode): string | undef
if (node.type === 'ThisExpression') {
return 'this';
}
if (node.type === 'MemberExpression' && !node.computed && node.property.type === 'Identifier') {
if (node.type === 'MemberExpression' && !node.computed) {
if (ctx.visitedNodes.has(node)) {
return;
}
ctx.visitedNodes.add(node);

if (node.property.name === '$') {
let property;
if (node.property.type === 'Identifier' && node.property.name !== '$') {
property = node.property.name;
} else if (node.property.type === 'PrivateName') {
property = `#${node.property.id.name}`;
} else if (node.property.type === 'PrivateIdentifier') {
property = `#${node.property.name}`;
} else {
return;
}

const lhs = tryFindExternalChain(ctx, node.object);
if (lhs) {
return `${lhs}.${node.property.name}`;
return `${lhs}.${property}`;
}
}
}
31 changes: 30 additions & 1 deletion packages/tinyest-for-wgsl/tests/parsers.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import babel from '@babel/parser';
import type { Node } from '@babel/types';
import type { ClassDeclaration, ClassProperty, Expression, Node } from '@babel/types';
import * as acorn from 'acorn';
import { describe, expect, it } from 'vitest';
import { transpileFn } from '../src/parsers.ts';
Expand Down Expand Up @@ -346,4 +346,33 @@ describe('transpileFn', () => {
);
}),
);

it(
'handles private property access',
dualTest((p) => {
// `this.#v` is only valid inside a class body, so we parse a class and pluck out the arrow function.
const tree = p(`
class Foo {
#v = 0;
fn = () => {
const k = this.#v;
};
}
`) as ClassDeclaration | acorn.Program;
const cls = (tree.type === 'Program' ? tree.body[0] : tree) as
| ClassDeclaration
| acorn.ClassDeclaration;
const props = cls.body.body;
const lastProp = props.at(-1) as ClassProperty | acorn.PropertyDefinition;
const fn = lastProp.value as Expression | acorn.Expression;

const { externalNames } = transpileFn(fn);

expect(externalNames).toMatchInlineSnapshot(`
Set {
"this.#v",
}
`);
}),
);
});
59 changes: 59 additions & 0 deletions packages/typegpu/tests/externalPropAccess.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,63 @@ describe('external prop access', () => {
}"
`);
});

it('supports private property access', () => {
class Cls {
#const = tgpu.const(d.u32, 1);

fn = () => {
'use gpu';
const a = this.#const.$;
};
Comment thread
aleksanderkatan marked this conversation as resolved.
}

const cls = new Cls();

expect(tgpu.resolve([cls.fn])).toMatchInlineSnapshot(`
"const const_1: u32 = 1u;

fn fn_1() {
const a = const_1;
}"
`);
});

it('supports private property access in anonymous class', () => {
const cls = new (class {
#const = tgpu.const(d.u32, 1);

fn = () => {
'use gpu';
const a = this.#const.$;
};
})();

expect(tgpu.resolve([cls.fn])).toMatchInlineSnapshot(`
"const const_1: u32 = 1u;

fn fn_1() {
const a = const_1;
}"
`);
});

it('supports props containing #', () => {
const cls = new (class {
'#const' = tgpu.const(d.u32, 0);

fn = () => {
'use gpu';
const a = this['#const'].$;
};
})();

expect(tgpu.resolve([cls.fn])).toMatchInlineSnapshot(`
"const item: u32 = 0u;

fn fn_1() {
const a = item;
}"
`);
});
});
17 changes: 16 additions & 1 deletion packages/unplugin-typegpu/src/core/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,15 @@ function extractLabelledExpression(path: NodePath): [string, NodePath<t.Expressi
// key = value;
// }
return [path.node.key.name, path.get('value') as NodePath<t.Expression>];
} else if (
path.node.type === 'ClassPrivateProperty' &&
path.node.value &&
path.node.key.type === 'PrivateName'
) {
// class Class {
// #key = value;
// }
return [path.node.key.id.name, path.get('value') as NodePath<t.Expression>];
}
}

Expand Down Expand Up @@ -380,7 +389,7 @@ function tryFindIdentifier(node: t.Node): string | undefined {

/**
* Checks if `node` contains a label and a tgpu expression that could be named.
* If so, it calls the provided callback. Nodes selected for naming include:
* If so, it calls the provided callback. Nodes selected for naming include (but are not limited to):
*
* `let name = tgpu.bindGroupLayout({});` (VariableDeclarator)
*
Expand Down Expand Up @@ -485,6 +494,12 @@ export const functionVisitor: TraverseOptions<PluginState> = {
);
},

ClassPrivateProperty(path, state) {
performExpressionNaming(state, path, (pathToName, name) =>
state.wrapInAutoName(pathToName, name),
);
},

AssignmentExpression: {
exit(path, state) {
const runtimeFn = operators[path.node.operator as keyof typeof operators];
Expand Down
87 changes: 87 additions & 0 deletions packages/unplugin-typegpu/test/auto-naming.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,47 @@ describe('[BABEL] auto naming', () => {
`);
});

it('works with class private properties', () => {
const code = `\
class Foo {
#const = tgpu.const(d.u32, 1);
#buff;

constructor() {
this.#buff = root.createUniform(d.u32);
}
}
`;

expect(babelTransform(code, { autoNamingEnabled: true })).toMatchInlineSnapshot(`
"class Foo {
#const = /*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.const(d.u32, 1), "const");
#buff;
constructor() {
this.#buff = /*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(root.createUniform(d.u32), "buff");
}
}"
`);
});

it('works with anonymous classes', () => {
const code = `\
const cls = new (class {
myConst = tgpu.const(d.u32, 0);
#const = tgpu.const(d.u32, 1);
})();
console.log(cls);
`;

expect(babelTransform(code, { autoNamingEnabled: true })).toMatchInlineSnapshot(`
"const cls = new class {
myConst = /*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.const(d.u32, 0), "myConst");
#const = /*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.const(d.u32, 1), "const");
}();
console.log(cls);"
`);
});

it('works with object properties', () => {
const code = `\
import { tgpu } from 'typegpu';
Expand Down Expand Up @@ -853,6 +894,52 @@ describe('[ROLLUP] auto naming', () => {
`);
});

it('works with class private properties', async () => {
const code = `\
class Foo {
#const = tgpu.const(d.u32, 1);
#buff;

constructor() {
this.#buff = root.createUniform(d.u32);
}
}
console.log(Foo);
`;

expect(await rollupTransform(code, { autoNamingEnabled: true })).toMatchInlineSnapshot(`
"class Foo {
#const = (/*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.const(d.u32, 1), "const"));
#buff;

constructor() {
this.#buff = (/*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(root.createUniform(d.u32), "buff"));
}
}
console.log(Foo);
"
`);
});

it('works with anonymous classes', async () => {
const code = `\
const cls = new (class {
myConst = tgpu.const(d.u32, 0);
#const = tgpu.const(d.u32, 1);
})();
console.log(cls);
`;

expect(await rollupTransform(code, { autoNamingEnabled: true })).toMatchInlineSnapshot(`
"const cls = new (class {
myConst = (/*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.const(d.u32, 0), "myConst"));
#const = (/*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.const(d.u32, 1), "const"));
})();
console.log(cls);
"
`);
});

it('works with object properties', async () => {
const code = `\
import { tgpu } from 'typegpu';
Expand Down
30 changes: 30 additions & 0 deletions packages/unplugin-typegpu/test/nested-externals.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,4 +140,34 @@ describe('externals gathering', () => {
);
});
});

describe('private prop access', () => {
const code = `\
import tgpu, { d } from 'typegpu';

const cls = new (class {
#const = tgpu.const(d.u32, 1);

fn = () => {
'use gpu';
const a = this.#const.$;
};
})();

console.log(cls);`;

it('works for BABEL', () => {
expect(extractExternals(babelTransform(code))).toMatchInlineSnapshot(`
"{
"this.#const": () => this.#const
}"
`);
});

it('works for ROLLUP', async () => {
expect(extractExternals(await rollupTransform(code))).toMatchInlineSnapshot(
`"{"this.#const":() => this.#const}"`,
);
});
});
});
Loading