aboutsummaryrefslogtreecommitdiff
path: root/plugin
diff options
context:
space:
mode:
author_ov4 <ov4@ov4.org>2026-10-22 14:59:00 +0000
committer_ov4 <ov4@ov4.org>2026-10-22 14:59:00 +0000
commit463efecf6ddcea31efada01ddd8a7695604ab80f (patch)
tree893c77614b02888b2410bf12d7b8e3575cff6cc4 /plugin
parente57e63d0d2a0e4c38460d399a0c62d140a633706 (diff)
fix(plugin): escape typographic single quotes for PowerShell
PowerShell treats U+2018 to U+201B as single quotes too, so a path containing one ended the quoted string early. Double them like '. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'plugin')
-rw-r--r--plugin/src/runner.js4
-rw-r--r--plugin/test/runner.test.js2
2 files changed, 5 insertions, 1 deletions
diff --git a/plugin/src/runner.js b/plugin/src/runner.js
index a327c58..7b7c1e0 100644
--- a/plugin/src/runner.js
+++ b/plugin/src/runner.js
@@ -5,7 +5,9 @@ const path = require('node:path');
function quote(value, shell) {
if (/[\r\n\0]/.test(value)) throw new Error('Run paths must not contain line breaks or NUL characters.');
if (shell === 'cmd') return `"${value}"`;
- return "'" + value.replace(/'/g, shell === 'powershell' ? "''" : "'\\''") + "'";
+ // PowerShell also ends single-quoted strings at the typographic quotes U+2018 to U+201B.
+ if (shell === 'powershell') return "'" + value.replace(/['\u2018-\u201B]/g, m => m + m) + "'";
+ return "'" + value.replace(/'/g, "'\\''") + "'";
}
function buildRunCommand(compiler, file, cwd, shell) {
diff --git a/plugin/test/runner.test.js b/plugin/test/runner.test.js
index a22d103..c795b85 100644
--- a/plugin/test/runner.test.js
+++ b/plugin/test/runner.test.js
@@ -12,6 +12,8 @@ test('commands quote paths and only run after changing directory', () => {
'cd /d "D:\\My Files" && "C:\\My Tools\\cpsc.exe" "D:\\My Files\\test.cps" -r');
assert.equal(buildRunCommand('C:\\My Tools\\cpsc.exe', "D:\\Pat's Files\\test.cps", "D:\\Pat's Files", 'powershell'),
"Set-Location -LiteralPath 'D:\\Pat''s Files'; if ($?) { & 'C:\\My Tools\\cpsc.exe' 'D:\\Pat''s Files\\test.cps' -r }");
+ assert.equal(buildRunCommand('cpsc', 'Pat\u2019s \u2018x\u201B.cps', '.', 'powershell'),
+ "Set-Location -LiteralPath '.'; if ($?) { & 'cpsc' 'Pat\u2019\u2019s \u2018\u2018x\u201B\u201B.cps' -r }");
assert.throws(() => buildRunCommand('cpsc\nother', 'test.cps', '.', 'posix'), /line breaks/);
if (process.platform !== 'win32') {
const file = "/tmp/Pat's file $(echo unexpected) `echo unexpected`.cps";