GitNexus/gitnexus/scripts/patch-tree-sitter-swift.cjs
abhigyanpatwari 5308600c22 fix(swift): fix install path for tree-sitter-swift
Move tree-sitter-swift to optionalDependencies so npm doesn't abort
when its native build fails. The postinstall script now handles the
case where npm removes the package after a failed build by
re-installing with --ignore-scripts, then patching and rebuilding.

Also fixes JSON.parse crash on binding.gyp trailing commas and uses
createRequire for conditional imports in ESM context.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 16:00:18 +05:30

98 lines
3.8 KiB
JavaScript

#!/usr/bin/env node
/**
* WORKAROUND: tree-sitter-swift@0.6.0 binding.gyp build failure
*
* Background:
* tree-sitter-swift@0.6.0's binding.gyp contains an "actions" array that
* invokes `tree-sitter generate` to regenerate parser.c from grammar.js.
* This is intended for grammar developers, but the published npm package
* already ships pre-generated parser files (parser.c, scanner.c), so the
* actions are unnecessary for consumers. Since consumers don't have
* tree-sitter-cli installed, the actions always fail during `npm install`.
*
* Why we can't just upgrade:
* tree-sitter-swift@0.7.1 fixes this (removes postinstall, ships prebuilds),
* but it requires tree-sitter@^0.22.1. The upstream project pins tree-sitter
* to ^0.21.0 and all other grammar packages depend on that version.
* Upgrading tree-sitter would be a separate breaking change.
*
* How this workaround works:
* tree-sitter-swift is listed as an optionalDependency, so npm won't abort
* if its native build fails. However, npm may also remove the package
* entirely after a failed build. This script handles both cases:
*
* 1. If tree-sitter-swift exists but has no native binding:
* patch binding.gyp and rebuild
* 2. If tree-sitter-swift was removed by npm after build failure:
* re-install with --ignore-scripts, patch, and rebuild
*
* TODO: Remove this script when tree-sitter is upgraded to ^0.22.x,
* which allows using tree-sitter-swift@0.7.1+ directly.
*/
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
const nodeModules = path.join(__dirname, '..', 'node_modules');
const swiftDir = path.join(nodeModules, 'tree-sitter-swift');
const bindingPath = path.join(swiftDir, 'binding.gyp');
function patchAndRebuild() {
const content = fs.readFileSync(bindingPath, 'utf8');
if (content.includes('"actions"')) {
// Strip Python-style comments (#) and trailing commas before JSON parsing
// binding.gyp uses GYP format which allows both, but JSON.parse does not
const cleaned = content
.replace(/#[^\n]*/g, '')
.replace(/,\s*([}\]])/g, '$1');
const gyp = JSON.parse(cleaned);
if (gyp.targets && gyp.targets[0] && gyp.targets[0].actions) {
delete gyp.targets[0].actions;
fs.writeFileSync(bindingPath, JSON.stringify(gyp, null, 2) + '\n');
console.log('[tree-sitter-swift] Patched binding.gyp (removed actions array)');
}
}
// Check if native binding already exists
const bindingNode = path.join(swiftDir, 'build', 'Release', 'tree_sitter_swift_binding.node');
if (fs.existsSync(bindingNode)) {
return; // Already built
}
console.log('[tree-sitter-swift] Building native binding...');
execSync('npx node-gyp rebuild', {
cwd: swiftDir,
stdio: 'pipe',
timeout: 120000,
});
console.log('[tree-sitter-swift] Native binding built successfully');
}
try {
// Case 1: package exists (npm kept it despite failed build)
if (fs.existsSync(bindingPath)) {
patchAndRebuild();
process.exit(0);
}
// Case 2: package was removed by npm after build failure — re-install without scripts
if (!fs.existsSync(swiftDir)) {
console.log('[tree-sitter-swift] Package missing, re-installing with --ignore-scripts...');
execSync('npm install tree-sitter-swift@0.6.0 --ignore-scripts --no-save', {
cwd: path.join(__dirname, '..'),
stdio: 'pipe',
timeout: 60000,
});
}
if (fs.existsSync(bindingPath)) {
patchAndRebuild();
} else {
console.warn('[tree-sitter-swift] Could not install package. Swift support will be disabled.');
}
} catch (err) {
console.warn('[tree-sitter-swift] Could not build native binding:', err.message);
console.warn('[tree-sitter-swift] Swift files will be skipped during analysis.');
}