From e4182ca661d6cfdd4fff152136a70bb6941ff769 Mon Sep 17 00:00:00 2001 From: The Jared Wilcurt Date: Sat, 18 Jun 2022 10:40:41 -0400 Subject: [PATCH] Fix Windows double-quotes bug --- manual-testing.js | 6 +-- package-lock.json | 93 ++++++++++++++++++++++----------------- package.json | 3 +- src/library.js | 11 +++-- src/windows.vbs | 12 +++-- tests/e2e.js | 43 +++++++++++++++++- tests/src/library.test.js | 6 +-- 7 files changed, 118 insertions(+), 56 deletions(-) diff --git a/manual-testing.js b/manual-testing.js index 9ac6f5b..51bd25b 100644 --- a/manual-testing.js +++ b/manual-testing.js @@ -3,10 +3,10 @@ * @author TheJaredWilcurt */ -const timeLabel = 'Executed in:'; +const timeLabel = 'Executed in'; console.time(timeLabel); -let createDesktopShortcuts = require('./index.js'); +const createDesktopShortcuts = require('./index.js'); let success = createDesktopShortcuts({ linux: { @@ -25,7 +25,7 @@ let success = createDesktopShortcuts({ icon: '..\\..\\..\\PortableApps\\Koa11y_v3.0.0\\package.nw\\_img\\fav.ico', filePath: 'C:\\PortableApps\\Koa11y_v3.0.0\\Koa11y.exe', outputPath: '%USERPROFILE%\\Desktop', - arguments: '--my-argument -f \'other stuff\'', + arguments: '--my-argument -f "other stuff"', windowMode: 'maximized', hotkey: 'ALT+CTRL+F' } diff --git a/package-lock.json b/package-lock.json index 56c7ac4..bdf92c5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "create-desktop-shortcuts", - "version": "1.8.0", + "version": "1.9.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "create-desktop-shortcuts", - "version": "1.8.0", + "version": "1.9.0", "license": "MIT", "dependencies": { "which": "^2.0.2" @@ -20,6 +20,7 @@ "eslint-config-tjw-jsdoc": "^1.0.2", "eslint-plugin-jsdoc": "^39.3.2", "fs-extra": "8.1.0", + "get-windows-shortcut-properties": "^1.1.0", "jest": "24.9.0", "mock-fs": "^5.1.2" } @@ -1924,9 +1925,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001355", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001355.tgz", - "integrity": "sha512-Sd6pjJHF27LzCB7pT7qs+kuX2ndurzCzkpJl6Qct7LPSZ9jn0bkOA8mdgMgmqnQAWLVOOGjLpc+66V57eLtb1g==", + "version": "1.0.30001356", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001356.tgz", + "integrity": "sha512-/30854bktMLhxtjieIxsrJBfs2gTM1pel6MXKF3K+RdIVJZcsn2A2QdhsuR4/p9+R204fZw0zCBBhktX8xWuyQ==", "dev": true, "funding": [ { @@ -2397,9 +2398,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.160", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.160.tgz", - "integrity": "sha512-O1Z12YfyeX2LXYO7MdHIPazGXzLzQnr1ADW55U2ARQsJBPgfpJz3u+g3Mo2l1wSyfOCdiGqaX9qtV4XKZ0HNRA==", + "version": "1.4.161", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.161.tgz", + "integrity": "sha512-sTjBRhqh6wFodzZtc5Iu8/R95OkwaPNn7tj/TaDU5nu/5EFiQDtADGAXdR4tJcTEHlYfJpHqigzJqHvPgehP8A==", "dev": true }, "node_modules/emoji-regex": { @@ -2578,9 +2579,9 @@ } }, "node_modules/eslint": { - "version": "8.17.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.17.0.tgz", - "integrity": "sha512-gq0m0BTJfci60Fz4nczYxNAlED+sMcihltndR8t9t1evnU/azx53x3t2UHXC/uRjcbvRw/XctpaNygSTcQD+Iw==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.18.0.tgz", + "integrity": "sha512-As1EfFMVk7Xc6/CvhssHUjsAQSkpfXvUGMFC3ce8JDe6WvqCgRrLOBQbVpsBFr1X1V+RACOadnzVvcUS5ni2bA==", "dev": true, "dependencies": { "@eslint/eslintrc": "^1.3.0", @@ -2680,9 +2681,9 @@ } }, "node_modules/eslint-plugin-jsdoc": { - "version": "39.3.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-39.3.2.tgz", - "integrity": "sha512-RSGN94RYzIJS/WfW3l6cXzRLfJWxvJgNQZ4w0WCaxJWDJMigtwTsILEAfKqmmPkT2rwMH/s3C7G5ChDE6cwPJg==", + "version": "39.3.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-39.3.3.tgz", + "integrity": "sha512-K/DAjKRUNaUTf0KQhI9PvsF+Y3mGDx/j0pofXsJCQe/tmRsRweBIXR353c8nAro0lytZYEf7l0PluBpzKDiHxw==", "dev": true, "dependencies": { "@es-joy/jsdoccomment": "~0.31.0", @@ -3651,6 +3652,12 @@ "node": ">=0.10.0" } }, + "node_modules/get-windows-shortcut-properties": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-windows-shortcut-properties/-/get-windows-shortcut-properties-1.1.0.tgz", + "integrity": "sha512-nirfHOxphWClVbzg0Z+AGYSE2876v7B17xLpgOqn/tUY3Vt5wdVkOP5i24hAFMsTSUmcesc9VYjhpXVnaWbPdQ==", + "dev": true + }, "node_modules/getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", @@ -5954,12 +5961,12 @@ } }, "node_modules/normalize-package-data/node_modules/resolve": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", - "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", "dev": true, "dependencies": { - "is-core-module": "^2.8.1", + "is-core-module": "^2.9.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -7919,9 +7926,9 @@ } }, "node_modules/typescript": { - "version": "4.7.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.3.tgz", - "integrity": "sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==", + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", + "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", "dev": true, "peer": true, "bin": { @@ -9810,9 +9817,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001355", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001355.tgz", - "integrity": "sha512-Sd6pjJHF27LzCB7pT7qs+kuX2ndurzCzkpJl6Qct7LPSZ9jn0bkOA8mdgMgmqnQAWLVOOGjLpc+66V57eLtb1g==", + "version": "1.0.30001356", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001356.tgz", + "integrity": "sha512-/30854bktMLhxtjieIxsrJBfs2gTM1pel6MXKF3K+RdIVJZcsn2A2QdhsuR4/p9+R204fZw0zCBBhktX8xWuyQ==", "dev": true }, "capture-exit": { @@ -10190,9 +10197,9 @@ } }, "electron-to-chromium": { - "version": "1.4.160", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.160.tgz", - "integrity": "sha512-O1Z12YfyeX2LXYO7MdHIPazGXzLzQnr1ADW55U2ARQsJBPgfpJz3u+g3Mo2l1wSyfOCdiGqaX9qtV4XKZ0HNRA==", + "version": "1.4.161", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.161.tgz", + "integrity": "sha512-sTjBRhqh6wFodzZtc5Iu8/R95OkwaPNn7tj/TaDU5nu/5EFiQDtADGAXdR4tJcTEHlYfJpHqigzJqHvPgehP8A==", "dev": true }, "emoji-regex": { @@ -10334,9 +10341,9 @@ } }, "eslint": { - "version": "8.17.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.17.0.tgz", - "integrity": "sha512-gq0m0BTJfci60Fz4nczYxNAlED+sMcihltndR8t9t1evnU/azx53x3t2UHXC/uRjcbvRw/XctpaNygSTcQD+Iw==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.18.0.tgz", + "integrity": "sha512-As1EfFMVk7Xc6/CvhssHUjsAQSkpfXvUGMFC3ce8JDe6WvqCgRrLOBQbVpsBFr1X1V+RACOadnzVvcUS5ni2bA==", "dev": true, "requires": { "@eslint/eslintrc": "^1.3.0", @@ -10500,9 +10507,9 @@ } }, "eslint-plugin-jsdoc": { - "version": "39.3.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-39.3.2.tgz", - "integrity": "sha512-RSGN94RYzIJS/WfW3l6cXzRLfJWxvJgNQZ4w0WCaxJWDJMigtwTsILEAfKqmmPkT2rwMH/s3C7G5ChDE6cwPJg==", + "version": "39.3.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-39.3.3.tgz", + "integrity": "sha512-K/DAjKRUNaUTf0KQhI9PvsF+Y3mGDx/j0pofXsJCQe/tmRsRweBIXR353c8nAro0lytZYEf7l0PluBpzKDiHxw==", "dev": true, "requires": { "@es-joy/jsdoccomment": "~0.31.0", @@ -11153,6 +11160,12 @@ "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==", "dev": true }, + "get-windows-shortcut-properties": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-windows-shortcut-properties/-/get-windows-shortcut-properties-1.1.0.tgz", + "integrity": "sha512-nirfHOxphWClVbzg0Z+AGYSE2876v7B17xLpgOqn/tUY3Vt5wdVkOP5i24hAFMsTSUmcesc9VYjhpXVnaWbPdQ==", + "dev": true + }, "getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", @@ -12989,12 +13002,12 @@ }, "dependencies": { "resolve": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", - "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", "dev": true, "requires": { - "is-core-module": "^2.8.1", + "is-core-module": "^2.9.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" } @@ -14509,9 +14522,9 @@ "dev": true }, "typescript": { - "version": "4.7.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.3.tgz", - "integrity": "sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==", + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", + "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", "dev": true, "peer": true }, diff --git a/package.json b/package.json index e6f841a..836cae5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "create-desktop-shortcuts", "main": "index.js", - "version": "1.8.0", + "version": "1.9.0", "description": "Easy API to create desktop shortcuts with Node", "author": "The Jared Wilcurt", "keywords": [ @@ -38,6 +38,7 @@ "eslint-config-tjw-jsdoc": "^1.0.2", "eslint-plugin-jsdoc": "^39.3.2", "fs-extra": "8.1.0", + "get-windows-shortcut-properties": "^1.1.0", "jest": "24.9.0", "mock-fs": "^5.1.2" }, diff --git a/src/library.js b/src/library.js index 9ef25ac..06a3f39 100644 --- a/src/library.js +++ b/src/library.js @@ -161,10 +161,13 @@ const library = { let windowMode = windowModes[options.windows.windowMode] || windowModes.normal; let hotkey = options.windows.hotkey || ''; - // Double quotes must be escaped, VBScript uses double quotes as the escape character - args = args.split('"').join('""'); - comment = comment.split('"').join('""'); - hotkey = hotkey.split('"').join('""'); + // Double quotes (") are used as an escape character in VBScript, and are stripped out of any arguments passed in + function replaceDoubleQuotes (str) { + return str.split('"').join('__DOUBLEQUOTE__'); + } + args = replaceDoubleQuotes(args); + comment = replaceDoubleQuotes(comment); + hotkey = replaceDoubleQuotes(hotkey); if (!icon) { if ( diff --git a/src/windows.vbs b/src/windows.vbs index 2121dea..aa1a64a 100644 --- a/src/windows.vbs +++ b/src/windows.vbs @@ -5,15 +5,21 @@ option explicit +' Double quotes are stripped out of arguments, so we replace them with this keyword, before passing them in +' Then re-insert them in a way that VBScript permits, via Chr(34) +Function replaceDoubleQuotes(str) + replaceDoubleQuotes = Replace(str, "__DOUBLEQUOTE__", Chr(34)) +End Function + dim strOutputPath, strFilePath, strArgs, strComment, strCwd, strIcon, strWindowMode, strHotkey strOutputPath = Wscript.Arguments(0) strFilePath = Wscript.Arguments(1) -strArgs = Wscript.Arguments(2) -strComment = Wscript.Arguments(3) +strArgs = replaceDoubleQuotes(Wscript.Arguments(2)) +strComment = replaceDoubleQuotes(Wscript.Arguments(3)) strCwd = Wscript.Arguments(4) strIcon = Wscript.Arguments(5) strWindowMode = Wscript.Arguments(6) -strHotkey = Wscript.Arguments(7) +strHotkey = replaceDoubleQuotes(Wscript.Arguments(7)) sub createFile() dim objShell, objLink diff --git a/tests/e2e.js b/tests/e2e.js index 98626b2..aa28520 100644 --- a/tests/e2e.js +++ b/tests/e2e.js @@ -8,6 +8,7 @@ console.time(timeLabel); const fs = require('fs-extra'); const path = require('path'); +const getWindowsShortcutProperties = require('get-windows-shortcut-properties'); const createDesktopShortcuts = require('../index.js'); @@ -21,6 +22,9 @@ let extension = extensions[process.platform] || ''; const filePath = path.join(__dirname, 'src'); const outputPath = path.join(__dirname, '__mocks__'); const outputFile = path.join(__dirname, '__mocks__', 'src' + extension); +const Arguments = '"test"'; +const hotkey = 'Ctrl+Shift+P'; +const comment = 'Some "very" good text.'; let success = createDesktopShortcuts({ linux: { @@ -34,7 +38,12 @@ let success = createDesktopShortcuts({ }, windows: { filePath, - outputPath + outputPath, + hotkey, + comment, + arguments: Arguments, + workingDirectory: outputPath, + windowMode: 'maximized' } }); @@ -77,7 +86,9 @@ function alert (pass, message) { console.log('\n ______________ ' + fill('_')); console.log('| |' + fill(' ') + '|'); console.log('| E2E ' + state + ' | ' + message + ' |'); - console.timeEnd(timeLabel); + if (process.platform !== 'win32') { + console.timeEnd(timeLabel); + } console.log('| |' + fill(' ') + '|'); console.log(' ¯¯¯¯¯¯¯¯¯¯¯¯¯¯ ' + fill('¯') + '\n\n'); @@ -89,6 +100,34 @@ function alert (pass, message) { if (success) { if (!fs.existsSync(outputFile)) { alert(false, 'Could not find desktop shortcut.'); + } else if (process.platform === 'win32') { + // We need to log the Windows time now to be accurate, as the + // getWindowsShortcutProperties step adds ~200-400ms that we don't care about + console.log('\n ______________ __________________________'); + console.log('| | |'); + console.log('| WINDOWS TIME | |'); + console.timeEnd(timeLabel); + console.log(' ¯¯¯¯¯¯¯¯¯¯¯¯¯¯ ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯' + '\n\n'); + // This is here to validate the VBS script outputted a shortcut as expected + const outputProperties = getWindowsShortcutProperties.sync(outputFile)[0]; + const expected = { + FullName: outputFile, + Arguments: Arguments, + Description: comment, + Hotkey: hotkey, + IconLocation: filePath + ',0', + RelativePath: '', + TargetPath: filePath, + WindowStyle: '3', + WorkingDirectory: outputPath + }; + const windowsShortcutVerified = JSON.stringify(expected) === JSON.stringify(outputProperties); + if (windowsShortcutVerified) { + alert(true, 'Successly created and validated file.'); + } else { + alert(false, 'Windows Shortcut properties mismatch'); + console.log({ expected, outputProperties }); + } } else { alert(true, 'Successly created and validated file.'); } diff --git a/tests/src/library.test.js b/tests/src/library.test.js index 0794526..65b5e97 100644 --- a/tests/src/library.test.js +++ b/tests/src/library.test.js @@ -475,7 +475,7 @@ describe('library', () => { library.produceWindowsVBSPath(), 'C:/Users/DUMMY/Desktop/file.lnk', 'C:/file.ext', - '-m ""Some text""', + '-m __DOUBLEQUOTE__Some text__DOUBLEQUOTE__', '', '', 'C:/file.ext', @@ -505,7 +505,7 @@ describe('library', () => { 'C:/Users/DUMMY/Desktop/file.lnk', 'C:/file.ext', '', - 'Look at what ""I"" made.', + 'Look at what __DOUBLEQUOTE__I__DOUBLEQUOTE__ made.', '', 'C:/file.ext', 1, @@ -538,7 +538,7 @@ describe('library', () => { '', 'C:/file.ext', 1, - 'CTRL+SHIFT+""' + 'CTRL+SHIFT+__DOUBLEQUOTE__' ] ); });