diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 18d2b470f..1e36e6486 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -77,6 +77,7 @@ jobs: registry-url: "https://registry.npmjs.org" - run: npm ci + - run: npm install --include=optional - run: opam install dune cppo - run: npm run compile - run: npm run bundle @@ -114,11 +115,20 @@ jobs: mv rescript-tools.exe ${{matrix.artifact-folder}} tar -cvf binary.tar ${{matrix.artifact-folder}} - - uses: actions/upload-artifact@v4 + - name: Upload binaries + uses: actions/upload-artifact@v4 with: name: ${{matrix.artifact-folder}} path: binary.tar + - name: Upload platform bindings + if: always() + uses: actions/upload-artifact@v4 + with: + name: bindings-${{matrix.artifact-folder}} + path: node_modules/@oxc-parser/ + retention-days: 1 + package: needs: - build @@ -134,6 +144,7 @@ jobs: registry-url: "https://registry.npmjs.org" - run: npm ci + - run: npm install --include=optional - run: npm run compile - name: Download MacOS binaries @@ -180,6 +191,13 @@ jobs: run: rm binary.tar working-directory: binaries + - name: Download platform bindings from all platforms + uses: actions/download-artifact@v4 + with: + pattern: bindings-* + path: bindings + merge-multiple: true + - name: Move binaries to folders run: | declare -a platforms=("darwin" "darwinarm64" "linux" "linuxarm64" "win32") @@ -194,6 +212,16 @@ jobs: mv binaries/"$platform"/rescript-tools.exe tools/binaries/"$platform" done + - name: Merge platform bindings into node_modules + run: | + mkdir -p node_modules/@oxc-parser + # Copy all bindings from downloaded artifacts + if [ -d "bindings" ]; then + find bindings -type d -name "binding-*" -exec cp -r {} node_modules/@oxc-parser/ \; + fi + # Ensure we have the Linux binding from current platform + npm install --include=optional || true + - name: Store short commit SHA for filename id: vars env: @@ -215,15 +243,18 @@ jobs: - name: Package Extension if: github.ref != 'refs/heads/master' - run: npx vsce package -o rescript-vscode-${{ steps.vars.outputs.sha_short }}.vsix + run: npx vsce package --no-yarn -o rescript-vscode-${{ steps.vars.outputs.sha_short }}.vsix - name: Package Extension pre-release version if: github.ref == 'refs/heads/master' - run: npx vsce package -o rescript-vscode-latest-master.vsix ${{ steps.increment_pre_release.outputs.new_version }} --no-git-tag-version + run: npx vsce package --no-yarn -o rescript-vscode-latest-master.vsix ${{ steps.increment_pre_release.outputs.new_version }} --no-git-tag-version - name: Package Extension release version if: startsWith(github.ref, 'refs/tags/') - run: npx vsce package -o rescript-vscode-${{ steps.tag_name.outputs.tag }}.vsix ${{ steps.tag_name.outputs.tag }} --no-git-tag-version + run: npx vsce package --no-yarn -o rescript-vscode-${{ steps.tag_name.outputs.tag }}.vsix ${{ steps.tag_name.outputs.tag }} --no-git-tag-version + + - name: Verify Package Contents + run: npm run verify-package - uses: actions/upload-artifact@v4 if: github.ref != 'refs/heads/master' diff --git a/package-lock.json b/package-lock.json index 199a17ae0..c9dc5e3a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "license": "MIT", "dependencies": { "magic-string": "^0.30.21", - "oxc-parser": "^0.97.0", + "oxc-parser": "0.97.0", "oxc-walker": "^0.5.2", "semver": "^7.7.2" }, diff --git a/package.json b/package.json index d66e011c9..23a8084a3 100644 --- a/package.json +++ b/package.json @@ -263,6 +263,7 @@ "scripts": { "clean": "rm -rf client/out server/out", "vscode:prepublish": "npm run clean && npm run bundle", + "verify-package": "node scripts/verify-package.mjs", "compile": "tsc -b", "watch": "tsc -b -w", "postinstall": "cd server && npm i && cd ../client && npm i && cd ../tools && npm i && cd ../tools/tests && npm i && cd ../../analysis/tests && npm i && cd ../reanalyze/examples/deadcode && npm i && cd ../termination && npm i", @@ -280,7 +281,7 @@ }, "dependencies": { "magic-string": "^0.30.21", - "oxc-parser": "^0.97.0", + "oxc-parser": "0.97.0", "oxc-walker": "^0.5.2", "semver": "^7.7.2" }, diff --git a/scripts/verify-package.mjs b/scripts/verify-package.mjs new file mode 100644 index 000000000..cbd9269c4 --- /dev/null +++ b/scripts/verify-package.mjs @@ -0,0 +1,115 @@ +#!/usr/bin/env node + +/** + * Script to verify that platform-specific native bindings and required dependencies + * are included in the packaged .vsix file + */ + +import fs from "fs"; +import path from "path"; +import { fileURLToPath } from "url"; +import { execSync } from "child_process"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const ROOT_DIR = path.join(__dirname, ".."); + +// Find .vsix file +const vsixFiles = fs + .readdirSync(ROOT_DIR) + .filter((f) => f.endsWith(".vsix")) + .sort((a, b) => { + const statA = fs.statSync(path.join(ROOT_DIR, a)); + const statB = fs.statSync(path.join(ROOT_DIR, b)); + return statB.mtimeMs - statA.mtimeMs; // Most recent first + }); + +if (vsixFiles.length === 0) { + console.error('No .vsix file found. Run "npx vsce package" first.'); + process.exit(1); +} + +const vsixFile = vsixFiles[0]; +console.log(`Checking ${vsixFile}...\n`); + +// Extract and check contents +const tempDir = path.join(ROOT_DIR, ".vsix-check"); +try { + fs.mkdirSync(tempDir, { recursive: true }); + + // .vsix is just a zip file + execSync(`unzip -q "${path.join(ROOT_DIR, vsixFile)}" -d "${tempDir}"`, { + stdio: "inherit", + }); + + const extensionDir = path.join(tempDir, "extension"); + const oxcParserDir = path.join(extensionDir, "node_modules", "@oxc-parser"); + + // Platform-specific bindings that should be included + const platformBindings = [ + "@oxc-parser/binding-darwin-arm64", + "@oxc-parser/binding-darwin-x64", + "@oxc-parser/binding-linux-x64-gnu", + "@oxc-parser/binding-win32-x64-msvc", + ]; + + const checks = [ + { + name: "oxc-parser", + path: path.join(extensionDir, "node_modules", "oxc-parser"), + required: true, + }, + ...platformBindings.map((binding) => ({ + name: binding, + path: path.join(oxcParserDir, binding.replace("@oxc-parser/", "")), + required: true, + })), + ]; + + let allGood = true; + let foundCount = 0; + + for (const check of checks) { + const exists = fs.existsSync(check.path); + const status = exists ? "✓" : "✗"; + console.log(`${status} ${check.name}: ${exists ? "FOUND" : "MISSING"}`); + + if (exists) { + foundCount++; + // Check for key files in bindings + if (check.name.startsWith("@oxc-parser/binding-")) { + // Look for .node files (native bindings) or package.json + const packageJson = path.join(check.path, "package.json"); + if (fs.existsSync(packageJson)) { + console.log(` ✓ package.json found`); + } else { + console.log(` ✗ package.json missing`); + allGood = false; + } + } + } else if (check.required) { + allGood = false; + } + } + + console.log(""); + console.log(`Found ${foundCount}/${checks.length} required packages`); + + if (allGood && foundCount === checks.length) { + console.log("✓ All required files are included in the package!"); + } else { + console.log("✗ Some required files are missing!"); + if (foundCount < platformBindings.length) { + console.log( + ` Warning: Only ${foundCount - 1} platform bindings found, expected ${platformBindings.length}`, + ); + console.log(" The extension may not work on all platforms."); + } + process.exit(1); + } +} finally { + // Cleanup + if (fs.existsSync(tempDir)) { + fs.rmSync(tempDir, { recursive: true, force: true }); + } +}