Skip to content

Commit 8279714

Browse files
committed
Create a browser variant of the rolldown plugin
1 parent 7fc452e commit 8279714

File tree

8 files changed

+413
-98
lines changed

8 files changed

+413
-98
lines changed

apps/typegpu-docs/public/coi-serviceworker.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ if (typeof window === 'undefined') {
3535

3636
self.addEventListener('fetch', (event) => {
3737
const r = event.request;
38-
console.log('SERVICE: Intercepting request...', r);
3938
if (r.cache === 'only-if-cached' && r.mode !== 'same-origin') {
4039
return;
4140
}

apps/typegpu-docs/src/components/translator/lib/tgslExecutor.ts

Lines changed: 17 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,6 @@
1-
import * as Babel from '@babel/standalone';
2-
import plugin from 'unplugin-typegpu/babel';
1+
import rolldownPlugin from 'unplugin-typegpu/rolldown-browser';
32
import { bundle } from './rolldown.ts';
43

5-
function translateTGSL(
6-
code: string,
7-
): string {
8-
const result = Babel.transform(code, {
9-
'presets': ['typescript'],
10-
'filename': 'example.ts',
11-
plugins: [plugin],
12-
}).code;
13-
return result || '';
14-
}
15-
164
const moduleImports = {
175
'typegpu': 'https://esm.sh/typegpu@latest/?bundle=false',
186
'typegpu/data': 'https://esm.sh/typegpu@latest/data/?bundle=false',
@@ -22,19 +10,24 @@ const moduleImports = {
2210
type TgslModule = Record<string, unknown>;
2311

2412
async function executeTgslModule(tgslCode: string): Promise<TgslModule> {
25-
const translatedCode = translateTGSL(tgslCode);
26-
27-
const { output } = await bundle(
13+
const result = await bundle(
2814
{
29-
'/utils.ts':
30-
'export function add(a: number, b: number): number { return a + b; }',
31-
'/index.ts':
32-
`import {add} from './utils.ts'; console.log('Hello, World!');`,
15+
'/shader.js': tgslCode,
16+
'/index.ts': `
17+
import tgpu from 'typegpu';
18+
import * as exports from './shader.js';
19+
20+
const shaderCode = tgpu.resolve({ externals: exports });
21+
export default shaderCode;
22+
`,
3323
},
3424
['./index.ts'],
25+
{
26+
plugins: [rolldownPlugin({})],
27+
},
3528
);
3629

37-
console.log(output);
30+
const output = result.output['index.js'];
3831

3932
const importMap = { imports: moduleImports };
4033

@@ -44,7 +37,7 @@ async function executeTgslModule(tgslCode: string): Promise<TgslModule> {
4437
document.head.appendChild(importMapScript);
4538

4639
try {
47-
const userBlob = new Blob([translatedCode], { type: 'text/javascript' });
40+
const userBlob = new Blob([output], { type: 'text/javascript' });
4841
const userModuleUrl = URL.createObjectURL(userBlob);
4942

5043
const module = await import(userModuleUrl);
@@ -58,16 +51,8 @@ async function executeTgslModule(tgslCode: string): Promise<TgslModule> {
5851

5952
export async function executeTgslCode(tgslCode: string): Promise<string> {
6053
try {
61-
const exports = await executeTgslModule(tgslCode);
62-
63-
const tgpuModule = await import(
64-
//@ts-expect-error
65-
'https://esm.sh/typegpu@latest?bundle=false'
66-
);
67-
68-
return tgpuModule.default.resolve({
69-
externals: exports as Record<string, object>,
70-
});
54+
const shaderCode = await executeTgslModule(tgslCode);
55+
return shaderCode.default as string;
7156
} catch (error) {
7257
throw new Error(
7358
`Failed to execute TGSL code: ${

packages/unplugin-typegpu/package.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"./esbuild": "./src/esbuild.ts",
2727
"./farm": "./src/farm.ts",
2828
"./rolldown": "./src/rolldown.ts",
29+
"./rolldown-browser": "./src/rolldown-browser.ts",
2930
"./rspack": "./src/rspack.ts",
3031
"./vite": "./src/vite.ts",
3132
"./webpack": "./src/webpack.ts"
@@ -79,6 +80,12 @@
7980
"import": "./dist/rolldown.js",
8081
"default": "./dist/rolldown.cjs"
8182
},
83+
"./rolldown-browser": {
84+
"types": "./dist/rolldown-browser.d.ts",
85+
"module": "./dist/rolldown-browser.js",
86+
"import": "./dist/rolldown-browser.js",
87+
"default": "./dist/rolldown-browser.cjs"
88+
},
8289
"./rspack": {
8390
"types": "./dist/rspack.d.ts",
8491
"module": "./dist/rspack.js",
@@ -138,6 +145,7 @@
138145
"@types/bun": "^1.2.19",
139146
"@types/picomatch": "^4.0.1",
140147
"acorn": "^8.14.1",
148+
"rolldown": "1.0.0-beta.33",
141149
"rollup": "~4.37.0",
142150
"tsup": "catalog:build",
143151
"typescript": "catalog:types"

packages/unplugin-typegpu/src/bun.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export default (rawOptions: Options): BunPlugin => {
1717
}
1818

1919
return {
20-
name: 'TypeGPU',
20+
name: 'unplugin-typegpu',
2121
setup(build) {
2222
build.onLoad({ filter: include }, async (args) => {
2323
const text = await Bun.file(args.path).text();

packages/unplugin-typegpu/src/common.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type * as babel from '@babel/types';
22
import type * as acorn from 'acorn';
3+
import type { MagicStringAST } from 'magic-string-ast';
34
import type { FilterPattern } from 'unplugin';
45

56
export type Context = {
@@ -243,6 +244,69 @@ export function performExpressionNaming<T extends acorn.AnyNode | babel.Node>(
243244
}
244245
}
245246

247+
export type FunctionNode =
248+
| acorn.FunctionDeclaration
249+
| acorn.AnonymousFunctionDeclaration
250+
| acorn.FunctionExpression
251+
| acorn.ArrowFunctionExpression;
252+
253+
export function containsKernelDirective(node: FunctionNode): boolean {
254+
if (node.body.type === 'BlockStatement') {
255+
for (const statement of node.body.body) {
256+
if (
257+
statement.type === 'ExpressionStatement' &&
258+
statement.directive === kernelDirective
259+
) {
260+
return true;
261+
}
262+
}
263+
}
264+
return false;
265+
}
266+
267+
export function removeKernelDirective(node: FunctionNode) {
268+
const cloned = structuredClone(node);
269+
270+
if (cloned.body.type === 'BlockStatement') {
271+
cloned.body.body = cloned.body.body.filter(
272+
(statement) =>
273+
!(
274+
statement.type === 'ExpressionStatement' &&
275+
statement.directive === kernelDirective
276+
),
277+
);
278+
}
279+
280+
return cloned;
281+
}
282+
283+
export function assignMetadata(
284+
magicString: MagicStringAST,
285+
node: acorn.AnyNode,
286+
metadata: string,
287+
) {
288+
magicString.prependLeft(
289+
node.start,
290+
'(($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (',
291+
).appendRight(
292+
node.end,
293+
`), ${metadata}) && $.f)({}))`,
294+
);
295+
}
296+
297+
export function wrapInAutoName(
298+
magicString: MagicStringAST,
299+
node: acorn.Node,
300+
name: string,
301+
) {
302+
magicString
303+
.prependLeft(
304+
node.start,
305+
'((globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(',
306+
)
307+
.appendRight(node.end, `, "${name}"))`);
308+
}
309+
246310
export const kernelDirective = 'kernel';
247311

248312
/** Regular expressions used for early pruning (to avoid unnecessary parsing, which is expensive) */

packages/unplugin-typegpu/src/index.ts

Lines changed: 5 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -6,80 +6,21 @@ import { FORMAT_VERSION } from 'tinyest';
66
import { transpileFn } from 'tinyest-for-wgsl';
77
import { createUnplugin, type UnpluginInstance } from 'unplugin';
88
import {
9+
assignMetadata,
10+
containsKernelDirective,
911
type Context,
1012
defaultOptions,
1113
earlyPruneRegex,
1214
embedJSON,
15+
type FunctionNode,
1316
gatherTgpuAliases,
1417
isShellImplementationCall,
15-
kernelDirective,
1618
type Options,
1719
performExpressionNaming,
20+
removeKernelDirective,
21+
wrapInAutoName,
1822
} from './common.ts';
1923

20-
type FunctionNode =
21-
| acorn.FunctionDeclaration
22-
| acorn.AnonymousFunctionDeclaration
23-
| acorn.FunctionExpression
24-
| acorn.ArrowFunctionExpression;
25-
26-
function containsKernelDirective(node: FunctionNode): boolean {
27-
if (node.body.type === 'BlockStatement') {
28-
for (const statement of node.body.body) {
29-
if (
30-
statement.type === 'ExpressionStatement' &&
31-
statement.directive === kernelDirective
32-
) {
33-
return true;
34-
}
35-
}
36-
}
37-
return false;
38-
}
39-
40-
function removeKernelDirective(node: FunctionNode) {
41-
const cloned = structuredClone(node);
42-
43-
if (cloned.body.type === 'BlockStatement') {
44-
cloned.body.body = cloned.body.body.filter(
45-
(statement) =>
46-
!(
47-
statement.type === 'ExpressionStatement' &&
48-
statement.directive === kernelDirective
49-
),
50-
);
51-
}
52-
53-
return cloned;
54-
}
55-
56-
function assignMetadata(
57-
magicString: MagicStringAST,
58-
node: acorn.AnyNode,
59-
metadata: string,
60-
) {
61-
magicString.prependLeft(
62-
node.start,
63-
'(($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (',
64-
).appendRight(
65-
node.end,
66-
`), ${metadata}) && $.f)({}))`,
67-
);
68-
}
69-
70-
function wrapInAutoName(
71-
magicString: MagicStringAST,
72-
node: acorn.Node,
73-
name: string,
74-
) {
75-
magicString
76-
.prependLeft(
77-
node.start,
78-
'((globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(',
79-
)
80-
.appendRight(node.end, `, "${name}"))`);
81-
}
82-
8324
const typegpu: UnpluginInstance<Options, false> = createUnplugin(
8425
(rawOptions) => {
8526
const options = defu(rawOptions, defaultOptions);

0 commit comments

Comments
 (0)