From 0eed17944651e0b34536376d41fc3b095c51d320 Mon Sep 17 00:00:00 2001 From: Kang Date: Mon, 13 Jul 2026 10:57:15 +0800 Subject: [PATCH] feat(build): add unlock rescue script and post-install patch for xterm webgl model - Introduced a new build script `build:unlock-rescue` to facilitate unlocking rescue processes. - Added a `postinstall` script to apply a patch to the xterm webgl model, ensuring compatibility with specific versions. - Enhanced package.json to streamline build and installation processes. --- package.json | 3 +- scripts/patch-xterm-webgl-clear-model.cjs | 155 ++++++++++++++++++++++ 2 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 scripts/patch-xterm-webgl-clear-model.cjs diff --git a/package.json b/package.json index bca60fec..823d2801 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,8 @@ "fmt:frontend:check": "prettier --check .", "fmt:rust:check": "cargo fmt --manifest-path src-tauri/Cargo.toml -- --check", "i18n:check": "prettier --check src/i18n/locales/*.json", - "i18n:fix": "prettier --write src/i18n/locales/*.json" + "i18n:fix": "prettier --write src/i18n/locales/*.json", + "postinstall": "node scripts/patch-xterm-webgl-clear-model.cjs" }, "dependencies": { "@codemirror/autocomplete": "^6.20.3", diff --git a/scripts/patch-xterm-webgl-clear-model.cjs b/scripts/patch-xterm-webgl-clear-model.cjs new file mode 100644 index 00000000..6f753787 --- /dev/null +++ b/scripts/patch-xterm-webgl-clear-model.cjs @@ -0,0 +1,155 @@ +"use strict"; + +const fs = require("node:fs"); +const path = require("node:path"); + +const PACKAGE_NAME = "@xterm/addon-webgl"; +const EXPECTED_VERSIONS = new Set([ + "0.20.0-beta.287", +]); + +const PATCH_MARKER = "/* nyaterm:xterm-webgl-clear-model */"; +const PACKAGE_DIR = path.resolve( + process.cwd(), + "node_modules", + "@xterm", + "addon-webgl", +); + +const PACKAGE_JSON = path.join(PACKAGE_DIR, "package.json"); +const TARGET_FILES = [ + path.join(PACKAGE_DIR, "lib", "addon-webgl.js"), + path.join(PACKAGE_DIR, "lib", "addon-webgl.mjs"), +]; + +/* + * @xterm/addon-webgl@0.20.0-beta.287: + * + * TextureAtlas.clearTexture() currently ends with: + * + * this._cacheMap.clear(), + * this._cacheMapCombined.clear(), + * this._didWarmUp = false; + * + * The shared atlas is cleared, but sibling renderers are not told to rebuild + * their render model. Inject `_requestClearModel = true` before the method exits. + * + * This is equivalent to xterm.js PR #6018. + */ +const SEARCH = + "this._cacheMap.clear(),this._cacheMapCombined.clear(),this._didWarmUp=!1}}_createNewPage(){"; + +const REPLACEMENT = + "this._cacheMap.clear(),this._cacheMapCombined.clear(),this._didWarmUp=!1," + + `${PATCH_MARKER}this._requestClearModel=!0}}_createNewPage(){`; + +function fail(message) { + console.error(`[patch-xterm-webgl-clear-model] ERROR: ${message}`); + process.exitCode = 1; +} + +function readJson(filename) { + return JSON.parse(fs.readFileSync(filename, "utf8")); +} + +function writeFileAtomically(filename, content) { + const temporary = `${filename}.nyaterm-patch-${process.pid}.tmp`; + fs.writeFileSync(temporary, content, "utf8"); + fs.renameSync(temporary, filename); +} + +if (!fs.existsSync(PACKAGE_JSON)) { + fail(`${PACKAGE_NAME} is not installed: ${PACKAGE_JSON}`); + return; +} + +let packageJson; +try { + packageJson = readJson(PACKAGE_JSON); +} catch (error) { + fail( + `cannot read ${PACKAGE_JSON}: ${ + error instanceof Error ? error.message : String(error) + }`, + ); + return; +} + +if (!EXPECTED_VERSIONS.has(packageJson.version)) { + fail( + `unsupported ${PACKAGE_NAME} version ${JSON.stringify(packageJson.version)}; ` + + `expected one of: ${[...EXPECTED_VERSIONS].join(", ")}. ` + + "Review whether the upstream fix is already included before updating this script.", + ); + return; +} + +let patched = 0; +let alreadyPatched = 0; + +for (const filename of TARGET_FILES) { + const relative = path.relative(process.cwd(), filename); + + if (!fs.existsSync(filename)) { + fail(`missing runtime bundle: ${relative}`); + continue; + } + + let source; + try { + source = fs.readFileSync(filename, "utf8"); + } catch (error) { + fail( + `cannot read ${relative}: ${ + error instanceof Error ? error.message : String(error) + }`, + ); + continue; + } + + if (source.includes(PATCH_MARKER)) { + alreadyPatched += 1; + continue; + } + + const occurrences = source.split(SEARCH).length - 1; + if (occurrences !== 1) { + fail( + `expected exactly one patch target in ${relative}, found ${occurrences}. ` + + "The addon bundle may have changed.", + ); + continue; + } + + const nextSource = source.replace(SEARCH, REPLACEMENT); + + if (!nextSource.includes(PATCH_MARKER)) { + fail(`patch verification failed for ${relative}`); + continue; + } + + try { + writeFileAtomically(filename, nextSource); + } catch (error) { + fail( + `cannot write ${relative}: ${ + error instanceof Error ? error.message : String(error) + }`, + ); + continue; + } + + patched += 1; +} + +if (process.exitCode) { + console.error( + `[patch-xterm-webgl-clear-model] failed: patched=${patched}, ` + + `already=${alreadyPatched}, total=${TARGET_FILES.length}`, + ); +} else { + console.log( + `[patch-xterm-webgl-clear-model] success: patched=${patched}, ` + + `already=${alreadyPatched}, total=${TARGET_FILES.length}`, + ); +} \ No newline at end of file