|
| 1 | +#!/usr/bin/env node |
| 2 | + |
| 3 | +/** |
| 4 | + * Builds tree-sitter WASM grammar files for all supported languages. |
| 5 | + * Requires: tree-sitter-cli and emscripten (or docker). |
| 6 | + * |
| 7 | + * Usage: node scripts/fetch-grammars.cjs |
| 8 | + * |
| 9 | + * If tree-sitter CLI cannot build WASM (no emscripten), you can manually |
| 10 | + * place pre-built .wasm files in the grammars/ directory. |
| 11 | + * |
| 12 | + * Copied from posthog-vscode/scripts/fetch-grammars.js |
| 13 | + */ |
| 14 | + |
| 15 | +const { execSync } = require('child_process'); |
| 16 | +const fs = require('fs'); |
| 17 | +const path = require('path'); |
| 18 | + |
| 19 | +const GRAMMARS_DIR = path.join(__dirname, '..', 'grammars'); |
| 20 | + |
| 21 | +function hasCli() { |
| 22 | + try { |
| 23 | + execSync('npx tree-sitter --version', { stdio: 'pipe' }); |
| 24 | + return true; |
| 25 | + } catch { |
| 26 | + return false; |
| 27 | + } |
| 28 | +} |
| 29 | + |
| 30 | +function buildGrammar(grammarPkg, outputName, subDir) { |
| 31 | + const dest = path.join(GRAMMARS_DIR, outputName); |
| 32 | + if (fs.existsSync(dest) && fs.statSync(dest).size > 10000) { |
| 33 | + const size = (fs.statSync(dest).size / 1024).toFixed(0); |
| 34 | + console.log(` ✓ ${outputName} (${size}KB, cached)`); |
| 35 | + return true; |
| 36 | + } |
| 37 | + |
| 38 | + const tempDir = path.join(__dirname, '..', '.grammar-build'); |
| 39 | + if (!fs.existsSync(tempDir)) { |
| 40 | + fs.mkdirSync(tempDir, { recursive: true }); |
| 41 | + } |
| 42 | + |
| 43 | + // Strip version specifier from package name for the directory path |
| 44 | + const dirName = grammarPkg.replace(/@[\d.]+.*$/, ''); |
| 45 | + const grammarDir = path.join(tempDir, 'node_modules', dirName); |
| 46 | + if (!fs.existsSync(grammarDir)) { |
| 47 | + process.stdout.write(` ↓ Installing ${grammarPkg}...`); |
| 48 | + try { |
| 49 | + execSync(`npm install ${grammarPkg} --prefix "${tempDir}" --ignore-scripts`, { |
| 50 | + stdio: 'pipe', |
| 51 | + cwd: tempDir, |
| 52 | + }); |
| 53 | + console.log(' OK'); |
| 54 | + } catch (err) { |
| 55 | + console.log(` FAILED`); |
| 56 | + return false; |
| 57 | + } |
| 58 | + } |
| 59 | + |
| 60 | + const buildDir = subDir ? path.join(grammarDir, subDir) : grammarDir; |
| 61 | + process.stdout.write(` ⚙ Building ${outputName}...`); |
| 62 | + try { |
| 63 | + execSync(`npx tree-sitter build --wasm -o "${dest}"`, { |
| 64 | + stdio: 'pipe', |
| 65 | + cwd: buildDir, |
| 66 | + timeout: 120000, |
| 67 | + }); |
| 68 | + const size = (fs.statSync(dest).size / 1024).toFixed(0); |
| 69 | + console.log(` ${size}KB`); |
| 70 | + return true; |
| 71 | + } catch (err) { |
| 72 | + const stderr = err.stderr ? err.stderr.toString().trim() : ''; |
| 73 | + const stdout = err.stdout ? err.stdout.toString().trim() : ''; |
| 74 | + const msg = stderr || stdout || err.message || ''; |
| 75 | + console.log(` FAILED`); |
| 76 | + if (msg) { console.log(` → ${msg}`); } |
| 77 | + return false; |
| 78 | + } |
| 79 | +} |
| 80 | + |
| 81 | +function main() { |
| 82 | + if (!fs.existsSync(GRAMMARS_DIR)) { |
| 83 | + fs.mkdirSync(GRAMMARS_DIR, { recursive: true }); |
| 84 | + } |
| 85 | + |
| 86 | + // Copy the core tree-sitter runtime WASM |
| 87 | + const runtimeSrc = path.join(__dirname, '..', 'node_modules', 'web-tree-sitter', 'tree-sitter.wasm'); |
| 88 | + const altRuntimeSrc = path.join(__dirname, '..', '..', '..', 'node_modules', 'web-tree-sitter', 'tree-sitter.wasm'); |
| 89 | + const runtimeDest = path.join(GRAMMARS_DIR, 'tree-sitter.wasm'); |
| 90 | + const src = fs.existsSync(runtimeSrc) ? runtimeSrc : altRuntimeSrc; |
| 91 | + if (fs.existsSync(src)) { |
| 92 | + fs.copyFileSync(src, runtimeDest); |
| 93 | + const size = (fs.statSync(runtimeDest).size / 1024).toFixed(0); |
| 94 | + console.log(` ✓ tree-sitter.wasm runtime (${size}KB)`); |
| 95 | + } |
| 96 | + |
| 97 | + console.log('\nBuilding tree-sitter grammar WASM files...\n'); |
| 98 | + |
| 99 | + if (!hasCli()) { |
| 100 | + console.log('⚠ tree-sitter CLI not found. Install it:'); |
| 101 | + console.log(' npm install -g tree-sitter-cli\n'); |
| 102 | + console.log('Then re-run: node scripts/fetch-grammars.cjs'); |
| 103 | + process.exit(1); |
| 104 | + } |
| 105 | + |
| 106 | + let built = 0; |
| 107 | + |
| 108 | + // JavaScript — pinned to 0.23.1 for ABI v14 compatibility with web-tree-sitter@0.24.x |
| 109 | + if (buildGrammar('tree-sitter-javascript@0.23.1', 'tree-sitter-javascript.wasm')) built++; |
| 110 | + |
| 111 | + // TypeScript (has typescript/ and tsx/ sub-directories) |
| 112 | + if (buildGrammar('tree-sitter-typescript', 'tree-sitter-typescript.wasm', 'typescript')) built++; |
| 113 | + if (buildGrammar('tree-sitter-typescript', 'tree-sitter-tsx.wasm', 'tsx')) built++; |
| 114 | + |
| 115 | + // Python — pinned to 0.23.5 for ABI v14 compatibility with web-tree-sitter@0.24.x |
| 116 | + if (buildGrammar('tree-sitter-python@0.23.5', 'tree-sitter-python.wasm')) built++; |
| 117 | + |
| 118 | + // Go — pinned to 0.23.4 for ABI v14 compatibility with web-tree-sitter@0.24.x |
| 119 | + if (buildGrammar('tree-sitter-go@0.23.4', 'tree-sitter-go.wasm')) built++; |
| 120 | + |
| 121 | + // Ruby — pinned to 0.23.1 for ABI v14 compatibility with web-tree-sitter@0.24.x |
| 122 | + if (buildGrammar('tree-sitter-ruby@0.23.1', 'tree-sitter-ruby.wasm')) built++; |
| 123 | + |
| 124 | + // Cleanup temp dir |
| 125 | + const tempDir = path.join(__dirname, '..', '.grammar-build'); |
| 126 | + try { fs.rmSync(tempDir, { recursive: true, force: true }); } catch { /* */ } |
| 127 | + |
| 128 | + console.log(`\n${built} grammar(s) ready in grammars/`); |
| 129 | + |
| 130 | + if (built === 0) { |
| 131 | + console.log('\n⚠ No grammars were built. You may need emscripten installed.'); |
| 132 | + console.log(' See: https://emscripten.org/docs/getting_started/downloads.html'); |
| 133 | + console.log(' Or use Docker: tree-sitter build --wasm --docker'); |
| 134 | + } |
| 135 | +} |
| 136 | + |
| 137 | +main(); |
0 commit comments