Skip to content

Commit 000cac8

Browse files
authored
Simplify WASM API (#10)
* Simplify WASM init API to single init() function Signed-off-by: Guan-Ming (Wesley) Chiu <105915352+guan404ming@users.noreply.github.com> * Refactor WASM build process to consolidate targets Signed-off-by: Guan-Ming (Wesley) Chiu <105915352+guan404ming@users.noreply.github.com> --------- Signed-off-by: Guan-Ming (Wesley) Chiu <105915352+guan404ming@users.noreply.github.com>
1 parent efb11ac commit 000cac8

10 files changed

Lines changed: 62 additions & 90 deletions

File tree

.github/workflows/ci.yml

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,8 @@ jobs:
4040
- name: Install wasm-pack
4141
uses: jetli/wasm-pack-action@v0.4.0
4242

43-
- name: Build WASM (Node.js and Web)
44-
run: |
45-
wasm-pack build --target nodejs --out-dir ts/wasm
46-
wasm-pack build --target web --out-dir ts/wasm-web
47-
cp ts/wasm-web/sqlparser_rs_wasm.js ts/wasm/sqlparser_rs_wasm_web.js
48-
cp ts/wasm-web/sqlparser_rs_wasm_bg.wasm ts/wasm/sqlparser_rs_wasm_web_bg.wasm
49-
rm -rf ts/wasm-web
43+
- name: Build WASM
44+
run: wasm-pack build --target web --out-dir ts/wasm
5045

5146
- name: Build TypeScript
5247
working-directory: ts

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ node_modules/
77
# WASM build output
88
/pkg
99
/ts/wasm
10-
/ts/wasm-web
1110

1211
# TypeScript build output
1312
/ts/dist

scripts/build.sh

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,35 +6,23 @@ echo "=========================================="
66
echo "Building sqlparser-ts npm package"
77
echo "=========================================="
88

9-
# Get the script directory
109
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
1110
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
1211

1312
echo ""
1413
echo "Project directory: $PROJECT_DIR"
1514
echo ""
1615

17-
# Check for wasm-pack
1816
if ! command -v wasm-pack &> /dev/null; then
1917
echo "Error: wasm-pack is not installed."
2018
echo "Install it with: cargo install wasm-pack"
2119
echo "Or visit: https://rustwasm.github.io/wasm-pack/installer/"
2220
exit 1
2321
fi
2422

25-
# Build WASM for Node.js
26-
echo "Step 1a: Building WASM module for Node.js..."
23+
echo "Step 1: Building WASM module..."
2724
cd "$PROJECT_DIR"
28-
wasm-pack build --target nodejs --out-dir ts/wasm
29-
30-
# Build WASM for Web (browser)
31-
echo "Step 1b: Building WASM module for Web (browser)..."
32-
wasm-pack build --target web --out-dir ts/wasm-web
33-
34-
# Copy web wasm files to wasm directory with _web suffix
35-
cp ts/wasm-web/sqlparser_rs_wasm.js ts/wasm/sqlparser_rs_wasm_web.js
36-
cp ts/wasm-web/sqlparser_rs_wasm_bg.wasm ts/wasm/sqlparser_rs_wasm_web_bg.wasm
37-
rm -rf ts/wasm-web
25+
wasm-pack build --target web --out-dir ts/wasm
3826

3927
echo ""
4028
echo "Step 2: Installing npm dependencies..."
@@ -51,10 +39,10 @@ echo "Build complete!"
5139
echo "=========================================="
5240
echo ""
5341
echo "Output files:"
54-
echo " - WASM: ts/wasm/"
55-
echo " - ESM: ts/dist/esm/"
56-
echo " - CJS: ts/dist/cts/"
57-
echo " - Types: ts/dist/types/"
42+
echo " - WASM: ts/wasm/"
43+
echo " - ESM: ts/dist/index.mjs"
44+
echo " - CJS: ts/dist/index.cjs"
45+
echo " - Types: ts/dist/index.d.{mts,cts}"
5846
echo ""
5947
echo "To run tests: cd ts && npm test"
6048
echo "To publish: cd ts && npm publish"

ts/README.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@ npm install @guanmingchiu/sqlparser-ts
2525
## Usage
2626

2727
```typescript
28-
import { parse, format, validate } from '@guanmingchiu/sqlparser-ts';
28+
import { init, parse, format, validate } from '@guanmingchiu/sqlparser-ts';
29+
30+
// Initialize WASM module (must be called once before using any parser functions)
31+
await init();
2932

3033
// Parse SQL into AST
3134
const ast = parse('SELECT * FROM users');
@@ -41,6 +44,19 @@ const sql = format('select * from users');
4144
validate('SELECT * FROM users'); // ok
4245
```
4346

47+
### Vite Configuration
48+
49+
WASM packages must be excluded from Vite's dev server [dependency pre-bundling](https://github.com/vitejs/vite/discussions/9256). This only affects the dev server. Production builds use Rollup instead of esbuild and handle WASM files correctly.
50+
51+
```typescript
52+
// vite.config.ts
53+
export default defineConfig({
54+
optimizeDeps: {
55+
exclude: ['@guanmingchiu/sqlparser-ts'],
56+
},
57+
});
58+
```
59+
4460
### Working with AST
4561

4662
```typescript

ts/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@
2020
],
2121
"scripts": {
2222
"build": "npm run build:wasm && npm run build:ts",
23-
"build:wasm": "cd .. && wasm-pack build --target nodejs --out-dir ts/wasm && rm -f ts/wasm/.gitignore",
24-
"build:wasm:web": "cd .. && wasm-pack build --target web --out-dir ts/wasm-web",
23+
"build:wasm": "cd .. && wasm-pack build --target web --out-dir ts/wasm && rm -f ts/wasm/.gitignore",
2524
"build:ts": "tsdown",
2625
"test": "vitest run",
2726
"test:watch": "vitest",

ts/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*/
55

66
// Parser
7-
export { Parser, initWasm, ready, parse, validate, format } from './parser.js';
7+
export { Parser, init, parse, validate, format } from './parser.js';
88
export type { ParserOptions, DialectInput } from './parser.js';
99

1010
// Dialects

ts/src/parser.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { ParserError } from './types/errors.js';
44
import type { Statement } from './types/ast.js';
55
import { getWasmModule } from './wasm.js';
66

7-
export { initWasm, ready } from './wasm.js';
7+
export { init } from './wasm.js';
88

99
/** Dialect can be specified as a Dialect instance or a string name */
1010
export type DialectInput = Dialect | DialectName;

ts/src/wasm.ts

Lines changed: 26 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -13,76 +13,51 @@ export interface WasmModule {
1313

1414
let wasmModule: WasmModule | null = null;
1515
let initPromise: Promise<void> | null = null;
16-
let initStarted = false;
1716

1817
const isBrowser = typeof window !== 'undefined' && typeof process === 'undefined';
1918

20-
function startInit(): void {
21-
if (initStarted) return;
22-
initStarted = true;
23-
initPromise = initWasm().catch(() => {
24-
// Silently ignore init errors - they'll be thrown when APIs are used
25-
});
26-
}
27-
28-
// Start init on module load
29-
startInit();
30-
3119
/** Get initialized WASM module or throw */
3220
export function getWasmModule(): WasmModule {
3321
if (wasmModule) {
3422
return wasmModule;
3523
}
3624
throw new WasmInitError(
37-
'WASM module not yet initialized. Use the async import or wait for module to load: import("@guanmingchiu/sqlparser-ts").then(({ parse }) => parse(sql))'
25+
'WASM module not yet initialized. Call `await init()` before using the parser.'
3826
);
3927
}
4028

4129
/**
42-
* Wait for WASM module to be ready
43-
*/
44-
export async function ready(): Promise<void> {
45-
startInit();
46-
await initPromise;
47-
}
48-
49-
/**
50-
* Initialize the WASM module explicitly.
51-
* Usually not needed - the module auto-initializes on first use.
30+
* Initialize the WASM module. Must be called before using any parser functions.
31+
* Safe to call multiple times, subsequent calls are no-ops.
5232
*/
53-
export async function initWasm(): Promise<void> {
33+
export async function init(): Promise<void> {
5434
if (wasmModule) {
5535
return;
5636
}
5737

58-
if (isBrowser) {
59-
try {
60-
const wasmJsUrl = new URL('../wasm/sqlparser_rs_wasm_web.js', import.meta.url);
61-
const wasmBinaryUrl = new URL('../wasm/sqlparser_rs_wasm_web_bg.wasm', import.meta.url);
62-
63-
const wasm = await import(/* @vite-ignore */ wasmJsUrl.href);
64-
65-
if (typeof wasm.default === 'function') {
66-
await wasm.default({ module_or_path: wasmBinaryUrl });
38+
if (!initPromise) {
39+
initPromise = (async () => {
40+
try {
41+
const wasm = await import(/* @vite-ignore */ '../wasm/sqlparser_rs_wasm.js');
42+
43+
if (isBrowser) {
44+
const wasmUrl = new URL('../wasm/sqlparser_rs_wasm_bg.wasm', import.meta.url);
45+
await wasm.default({ module_or_path: wasmUrl });
46+
} else {
47+
const { readFile } = await import(/* @vite-ignore */ 'node:fs/promises');
48+
const wasmPath = new URL('../wasm/sqlparser_rs_wasm_bg.wasm', import.meta.url);
49+
const bytes = await readFile(wasmPath);
50+
await wasm.default({ module_or_path: bytes });
51+
}
52+
53+
wasmModule = wasm as WasmModule;
54+
} catch (error) {
55+
throw new WasmInitError(
56+
`Failed to load WASM module: ${error instanceof Error ? error.message : String(error)}`
57+
);
6758
}
68-
69-
wasmModule = wasm as WasmModule;
70-
} catch (error) {
71-
throw new WasmInitError(
72-
`Failed to load WASM module in browser: ${error instanceof Error ? error.message : String(error)}`
73-
);
74-
}
75-
return;
59+
})();
7660
}
7761

78-
// Node.js
79-
try {
80-
const wasmUrl = new URL('../wasm/sqlparser_rs_wasm.js', import.meta.url);
81-
const wasm = await import(/* @vite-ignore */ wasmUrl.href);
82-
wasmModule = wasm as WasmModule;
83-
} catch (error) {
84-
throw new WasmInitError(
85-
`Failed to load WASM module: ${error instanceof Error ? error.message : String(error)}`
86-
);
87-
}
62+
await initPromise;
8863
}

ts/tests/builds.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,22 +60,22 @@ describe('CJS build', () => {
6060
});
6161

6262
test('parse works', async () => {
63-
const { parse, ready } = require('../dist/index.cjs');
64-
await ready();
63+
const { parse, init } = require('../dist/index.cjs');
64+
await init();
6565
const result = parse('SELECT 1');
6666
expect(Array.isArray(result)).toBe(true);
6767
});
6868

6969
test('format works', async () => {
70-
const { format, ready } = require('../dist/index.cjs');
71-
await ready();
70+
const { format, init } = require('../dist/index.cjs');
71+
await init();
7272
const result = format('select 1');
7373
expect(result).toBe('SELECT 1');
7474
});
7575

7676
test('dialect string works', async () => {
77-
const { parse, ready } = require('../dist/index.cjs');
78-
await ready();
77+
const { parse, init } = require('../dist/index.cjs');
78+
await init();
7979
const result = parse('SELECT $1', 'postgresql');
8080
expect(Array.isArray(result)).toBe(true);
8181
});

ts/tests/setup.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { initWasm } from '../src';
1+
import { init } from '../src';
22

33
// Initialize WASM before running tests
4-
await initWasm();
4+
await init();

0 commit comments

Comments
 (0)