11#! /bin/bash
22# Build Cursor ARM64 by grafting Cursor's JS resources onto VS Code ARM64.
33# Cursor is a closed-source VS Code fork — all proprietary code is JS (arch-independent).
4- # Native binaries (Electron, Node) come from the open-source VS Code ARM64 build .
4+ # Native binaries (Electron, Node, .node modules ) come from the open-source VS Code ARM64.
55#
6- # Cursor 2.x changed its AppImage structure:
7- # Old (0.x): squashfs-root/resources/app/...
8- # New (2.x): squashfs-root/usr/share/cursor/resources/app/...
9- # This script handles both layouts.
6+ # IMPORTANT: Cursor's node_modules contain x86 native .node files. After copying,
7+ # we overwrite them with VS Code ARM64's native modules (same ABI, same versions).
108set -euo pipefail
119
1210WORKDIR=" ${WORKDIR:-/ tmp/ cursor-build} "
@@ -27,6 +25,8 @@ OFFSET=$(grep -aobP 'hsqs' cursor-x86.AppImage | tail -1 | cut -d: -f1)
2725unsquashfs -o " $OFFSET " -d squashfs-root cursor-x86.AppImage
2826
2927# 3. Detect app root (handles both old and new AppImage layouts)
28+ # Old (0.x): squashfs-root/resources/app/...
29+ # New (2.x): squashfs-root/usr/share/cursor/resources/app/...
3030if [ -f " squashfs-root/usr/share/cursor/resources/app/product.json" ]; then
3131 APP_ROOT=" squashfs-root/usr/share/cursor"
3232elif [ -f " squashfs-root/resources/app/product.json" ]; then
@@ -47,9 +47,11 @@ echo "$CURSOR_VERSION" > "$OUTDIR/version.txt"
4747echo " ==> Downloading VS Code ARM64 $VSCODE_VERSION ..."
4848curl -Lo vscode-arm64.tar.gz \
4949 " https://update.code.visualstudio.com/${VSCODE_VERSION} /linux-arm64/stable"
50- rm -rf vscode-arm64
51- mkdir vscode-arm64
52- tar xzf vscode-arm64.tar.gz -C vscode-arm64 --strip-components=1
50+ rm -rf vscode-arm64 vscode-arm64-clean
51+ mkdir vscode-arm64-clean
52+ tar xzf vscode-arm64.tar.gz -C vscode-arm64-clean --strip-components=1
53+ # Keep a clean copy for native module extraction
54+ cp -a vscode-arm64-clean vscode-arm64
5355
5456# 6. Graft Cursor's proprietary JS onto VS Code ARM64
5557echo " ==> Grafting Cursor onto VS Code ARM64..."
@@ -67,26 +69,67 @@ for ext in "$APP_ROOT/resources/app/extensions/cursor-"* "$APP_ROOT/resources/ap
6769 [ -e " $ext " ] && cp -R " $ext " vscode-arm64/resources/app/extensions/
6870done
6971
70- # Node modules (both dir and asar may exist in 2.x)
71- rm -rf vscode-arm64/resources/app/node_modules vscode-arm64/resources/app/node_modules.asar
72- [ -d " $APP_ROOT /resources/app/node_modules" ] && \
73- cp -R " $APP_ROOT /resources/app/node_modules" vscode-arm64/resources/app/
74- [ -f " $APP_ROOT /resources/app/node_modules.asar" ] && \
75- cp " $APP_ROOT /resources/app/node_modules.asar" vscode-arm64/resources/app/
76-
7772# App resources (icons, etc.)
7873rm -rf vscode-arm64/resources/app/resources
7974cp -R " $APP_ROOT /resources/app/resources" vscode-arm64/resources/app/
8075
81- # 7. Rename binary: code → cursor
76+ # 7. Handle node_modules: copy Cursor's JS, then fix native modules
77+ echo " ==> Copying Cursor node_modules (JS code)..."
78+ rm -rf vscode-arm64/resources/app/node_modules
79+ cp -R " $APP_ROOT /resources/app/node_modules" vscode-arm64/resources/app/
80+
81+ # Copy node_modules.asar if present
82+ [ -f " $APP_ROOT /resources/app/node_modules.asar" ] && \
83+ cp " $APP_ROOT /resources/app/node_modules.asar" vscode-arm64/resources/app/
84+
85+ # Restore node_modules.asar.unpacked from VS Code ARM64
86+ rm -rf vscode-arm64/resources/app/node_modules.asar.unpacked
87+ [ -d " vscode-arm64-clean/resources/app/node_modules.asar.unpacked" ] && \
88+ cp -R " vscode-arm64-clean/resources/app/node_modules.asar.unpacked" vscode-arm64/resources/app/
89+
90+ # 8. Replace x86 native .node modules with ARM64 from VS Code
91+ echo " ==> Replacing x86 native modules with ARM64..."
92+ find vscode-arm64-clean/resources/app/node_modules -name " *.node" -type f | while read arm64_file; do
93+ relpath=" ${arm64_file# vscode-arm64-clean/ resources/ app/ node_modules/ } "
94+ target=" vscode-arm64/resources/app/node_modules/$relpath "
95+ if [ -f " $target " ]; then
96+ cp " $arm64_file " " $target "
97+ echo " replaced: $relpath "
98+ fi
99+ done
100+
101+ # Handle Cursor-only native modules:
102+ # @anysphere/policy-watcher → same as @vscode/policy-watcher
103+ VSCODE_PW=" vscode-arm64-clean/resources/app/node_modules/@vscode/policy-watcher/build/Release/vscode-policy-watcher.node"
104+ CURSOR_PW=" vscode-arm64/resources/app/node_modules/@anysphere/policy-watcher/build/Release/vscode-policy-watcher.node"
105+ if [ -f " $VSCODE_PW " ] && [ -f " $CURSOR_PW " ]; then
106+ cp " $VSCODE_PW " " $CURSOR_PW "
107+ echo " replaced: @anysphere/policy-watcher (from @vscode/policy-watcher)"
108+ fi
109+
110+ # cursor-proclist — x86-only, non-critical, remove
111+ PROCLIST=" vscode-arm64/resources/app/node_modules/cursor-proclist/build/Release/cursor_proclist.node"
112+ [ -f " $PROCLIST " ] && rm " $PROCLIST " && echo " removed: cursor-proclist (x86-only, non-critical)"
113+
114+ # 9. Verify no x86 .node files remain in node_modules
115+ echo " ==> Verifying architecture..."
116+ X86_COUNT=$( find vscode-arm64/resources/app/node_modules -name " *.node" -type f -exec file {} \; | grep -c " x86-64" || true)
117+ if [ " $X86_COUNT " -gt 0 ]; then
118+ echo " WARNING: $X86_COUNT x86-64 native modules still present:"
119+ find vscode-arm64/resources/app/node_modules -name " *.node" -type f -exec file {} \; | grep " x86-64"
120+ else
121+ echo " All clean — ARM64 only!"
122+ fi
123+
124+ # 10. Rename binary: code → cursor
82125echo " ==> Renaming binary..."
83126mv vscode-arm64/code vscode-arm64/cursor 2> /dev/null || true
84127mv vscode-arm64/bin/code vscode-arm64/bin/cursor 2> /dev/null || true
85128sed -i ' s|ELECTRON="$VSCODE_PATH/code"|ELECTRON="$VSCODE_PATH/cursor"|' vscode-arm64/bin/cursor
86129
87- # 8 . Extract icon
130+ # 11 . Extract icon
88131ICON=" "
89- for candidate in " $APP_ROOT /../co.anysphere.cursor.png " " squashfs-root/co.anysphere.cursor.png" " squashfs-root/cursor.png" ; do
132+ for candidate in " squashfs-root/co.anysphere.cursor.png" " squashfs-root/cursor.png" ; do
90133 [ -f " $candidate " ] && ICON=" $candidate " && break
91134done
92135[ -n " $ICON " ] && cp " $ICON " " $OUTDIR /cursor.png"
0 commit comments