diff --git a/auto_py_to_exe/ui.py b/auto_py_to_exe/ui.py index e2d4d2ea..7b2760b3 100644 --- a/auto_py_to_exe/ui.py +++ b/auto_py_to_exe/ui.py @@ -126,6 +126,14 @@ def is_file_an_ico(file_path): return False +@eel.expose +def convert_path_to_absolute(path: str) -> str: + """Converts a path to an absolute path if it exists. If it doesn't exist, returns the path as is.""" + if not os.path.exists(path): + return path + return os.path.abspath(path) + + @eel.expose def import_configuration(): """Get configuration data from a file""" diff --git a/auto_py_to_exe/web/index.html b/auto_py_to_exe/web/index.html index dc02f7f5..3782860d 100644 --- a/auto_py_to_exe/web/index.html +++ b/auto_py_to_exe/web/index.html @@ -20,6 +20,7 @@ ask_files: () => [], ask_folder: () => '', is_file_an_ico: (file_path) => null, + convert_path_to_absolute: (path) => '', open_output_in_explorer: (output_directory, input_filename, is_one_file) => {}, will_packaging_overwrite_existing: (file_path, manual_name, one_file, output_folder) => true, package: (command, non_pyinstaller_options) => {}, diff --git a/auto_py_to_exe/web/js/configuration.js b/auto_py_to_exe/web/js/configuration.js index 1ce8f296..efe5c52a 100644 --- a/auto_py_to_exe/web/js/configuration.js +++ b/auto_py_to_exe/web/js/configuration.js @@ -7,7 +7,7 @@ const configurationSetters = {}; // dest: fn(value) => void, used to set option const configurationCleaners = []; // Each function in this should clear a dest value // Get option-value pairs [[option, value], ...] -const getCurrentConfiguration = () => { +const getCurrentConfiguration = async (skipTransformations = false) => { const currentConfiguration = [ { optionDest: 'noconfirm', @@ -26,6 +26,33 @@ const getCurrentConfiguration = () => { } }); + if (skipTransformations) { + return currentConfiguration; + } + + // Convert all relative paths to absolute paths + for (const c of currentConfiguration) { + const option = options.find((o) => o.dest === c.optionDest); + if (option === undefined) { + continue; + } + + if ( + [OPTION_INPUT_VALUE_FILE, OPTION_INPUT_VALUE_DIRECTORY].some((v) => option.allowedInputValues.includes(v)) || + option.dest === 'filenames' + ) { + c.value = await convertPathToAbsolute(c.value); + } + if ( + [OPTION_INPUT_VALUE_DOUBLE_FILE_DEST, OPTION_INPUT_VALUE_DOUBLE_DIRECTORY_DEST].some((v) => + option.allowedInputValues.includes(v) + ) + ) { + const [src, dest] = c.value.split(pathSeparator); + c.value = `${await convertPathToAbsolute(src)}${pathSeparator}${dest}`; + } + } + return currentConfiguration; }; @@ -37,8 +64,8 @@ const getNonPyinstallerConfiguration = () => { }; }; -const getCurrentCommand = () => { - const currentConfiguration = getCurrentConfiguration(); +const getCurrentCommand = async () => { + const currentConfiguration = await getCurrentConfiguration(); // Match configuration values with the correct flags const optionsAndValues = currentConfiguration @@ -71,10 +98,10 @@ const getCurrentCommand = () => { } "${entryScript}"`; }; -const updateCurrentCommandDisplay = () => { - document.querySelector('#current-command textarea').value = getCurrentCommand(); +const updateCurrentCommandDisplay = async () => { + document.querySelector('#current-command textarea').value = await getCurrentCommand(); }; -const isCommandDefault = () => { - return getCurrentCommand() === 'pyinstaller --noconfirm --onedir --console ""'; +const isCommandDefault = async () => { + return (await getCurrentCommand()) === 'pyinstaller --noconfirm --onedir --console ""'; }; diff --git a/auto_py_to_exe/web/js/constants.js b/auto_py_to_exe/web/js/constants.js index 63b3b467..e684825d 100644 --- a/auto_py_to_exe/web/js/constants.js +++ b/auto_py_to_exe/web/js/constants.js @@ -2,7 +2,15 @@ const options_ignored = ['help']; const options_static = ['filenames', 'onefile', 'console', 'icon_file', 'datas']; const options_overridden = ['specpath', 'distpath', 'workpath', 'noconfirm']; -const options_inputTypeFile = ['runtime_hooks', 'version_file', 'manifest', 'resources', 'splash']; +const options_inputTypeFile = [ + 'runtime_hooks', + 'version_file', + 'manifest', + 'resources', + 'splash', + 'entitlements_file', + 'icon_file', +]; const options_inputTypeDirectory = ['upx_dir', 'pathex', 'hookspath']; const options_inputTypeDoubleFileDest = ['datas', 'binaries']; const options_inputTypeDoubleDirectoryDest = ['datas']; diff --git a/auto_py_to_exe/web/js/importExport.js b/auto_py_to_exe/web/js/importExport.js index c4bc53cf..c52221c0 100644 --- a/auto_py_to_exe/web/js/importExport.js +++ b/auto_py_to_exe/web/js/importExport.js @@ -21,19 +21,19 @@ const importConfiguration = (configuration) => { } }; -const _collectDataToExport = () => { +const _collectDataToExport = async () => { const nonPyinstallerConfiguration = getNonPyinstallerConfiguration(); delete nonPyinstallerConfiguration.outputDirectory; // This does not need to be saved in the config return { version: 'auto-py-to-exe-configuration_v1', - pyinstallerOptions: getCurrentConfiguration(), + pyinstallerOptions: await getCurrentConfiguration(true), nonPyinstallerOptions: nonPyinstallerConfiguration, }; }; const onConfigurationImport = async () => { - if (!isCommandDefault()) { + if (!(await isCommandDefault())) { const response = await displayModal( getTranslation('dynamic.modal.configModalTitle'), getTranslation('dynamic.modal.configModalDescription'), @@ -53,6 +53,6 @@ const onConfigurationImport = async () => { }; const onConfigurationExport = async () => { - const data = _collectDataToExport(); + const data = await _collectDataToExport(); await eel.export_configuration(data)(); }; diff --git a/auto_py_to_exe/web/js/initialise.js b/auto_py_to_exe/web/js/initialise.js index 01243d1d..19a63235 100644 --- a/auto_py_to_exe/web/js/initialise.js +++ b/auto_py_to_exe/web/js/initialise.js @@ -96,7 +96,7 @@ window.addEventListener('load', async () => { setupWarnings(initialisationData.warnings); // Update the current command when setup is complete - updateCurrentCommandDisplay(); + await updateCurrentCommandDisplay(); // Try to translate to the default browser language translate(initialisationData.languageHint); diff --git a/auto_py_to_exe/web/js/interface.js b/auto_py_to_exe/web/js/interface.js index 308fe6eb..40d3a29f 100644 --- a/auto_py_to_exe/web/js/interface.js +++ b/auto_py_to_exe/web/js/interface.js @@ -61,13 +61,13 @@ const addDoubleInputForSrcDst = ( sourceInput.value = source; sourceInput.addEventListener('input', (event) => { colourInput(sourceInput, false, sourceCanBeFile, sourceCanBeDirectory); - updateCurrentCommandDisplay(); + void updateCurrentCommandDisplay(); }); colourInput(sourceInput, false, sourceCanBeFile, sourceCanBeDirectory); destinationInput.value = destination; destinationInput.addEventListener('input', (event) => { - updateCurrentCommandDisplay(); + void updateCurrentCommandDisplay(); }); // Add configurationGetter @@ -81,13 +81,13 @@ const addDoubleInputForSrcDst = ( configurationGetters.splice(configurationGetterIndex, 1); const configurationCleanerIndex = configurationCleaners.indexOf(onRemove); configurationCleaners.splice(configurationCleanerIndex, 1); - updateCurrentCommandDisplay(); + void updateCurrentCommandDisplay(); }; removeButton.src = 'img/remove.svg'; removeButton.addEventListener('click', onRemove); configurationCleaners.push(onRemove); - updateCurrentCommandDisplay(); + void updateCurrentCommandDisplay(); }; const _createSubSectionInAdvanced = (title, i18nPath, options) => { @@ -147,7 +147,7 @@ const _createSubSectionInAdvanced = (title, i18nPath, options) => { enableButton.classList.add('unselected'); enableButton.classList.remove('selected'); } - updateCurrentCommandDisplay(); + void updateCurrentCommandDisplay(); }; // When clicked, toggle the value @@ -176,7 +176,7 @@ const _createSubSectionInAdvanced = (title, i18nPath, options) => { const selectNode = document.createElement('select'); container.appendChild(selectNode); selectNode.addEventListener('change', (event) => { - updateCurrentCommandDisplay(); + void updateCurrentCommandDisplay(); }); // Add options (including default '') @@ -225,7 +225,7 @@ const _createSubSectionInAdvanced = (title, i18nPath, options) => { container.appendChild(inputNode); inputNode.placeholder = o.metavar || 'VALUE'; inputNode.addEventListener('input', (event) => { - updateCurrentCommandDisplay(); + void updateCurrentCommandDisplay(); if (isOptionFileBased || isOptionDirectoryBased) { colourInput(inputNode, true, isOptionFileBased, isOptionDirectoryBased); @@ -296,7 +296,7 @@ const _createSubSectionInAdvanced = (title, i18nPath, options) => { colourInput(inputNode, false, isOptionFileBased, isOptionDirectoryBased); inputNode.addEventListener('input', (event) => { colourInput(inputNode, false, isOptionFileBased, isOptionDirectoryBased); - updateCurrentCommandDisplay(); + void updateCurrentCommandDisplay(); }); // Add configurationGetter @@ -313,14 +313,14 @@ const _createSubSectionInAdvanced = (title, i18nPath, options) => { configurationGetters.splice(configurationGetterIndex, 1); const configurationCleanerIndex = configurationCleaners.indexOf(onRemove); configurationCleaners.splice(configurationCleanerIndex, 1); - updateCurrentCommandDisplay(); + void updateCurrentCommandDisplay(); }; removeButtonNode.addEventListener('click', onRemove); // Add configurationCleaner configurationCleaners.push(onRemove); - updateCurrentCommandDisplay(); + void updateCurrentCommandDisplay(); }; // Event to add a new input pair diff --git a/auto_py_to_exe/web/js/packaging.js b/auto_py_to_exe/web/js/packaging.js index 8ea2457f..dcebd031 100644 --- a/auto_py_to_exe/web/js/packaging.js +++ b/auto_py_to_exe/web/js/packaging.js @@ -45,8 +45,8 @@ const setPackagingState = (newState) => { } }; -const startPackaging = () => { - eel.package(getCurrentCommand(), getNonPyinstallerConfiguration())(); +const startPackaging = async () => { + eel.package(await getCurrentCommand(), getNonPyinstallerConfiguration())(); setPackagingState(PACKAGING_STATE_PACKAGING); }; diff --git a/auto_py_to_exe/web/js/staticEvents.js b/auto_py_to_exe/web/js/staticEvents.js index 096f9e69..9ddd90dc 100644 --- a/auto_py_to_exe/web/js/staticEvents.js +++ b/auto_py_to_exe/web/js/staticEvents.js @@ -6,7 +6,7 @@ Handle user events const scriptLocationChange = async (event) => { colourInput(event.target, false, true, false); - updateCurrentCommandDisplay(); + void updateCurrentCommandDisplay(); }; const scriptLocationSearch = async (event) => { @@ -27,7 +27,7 @@ const oneFileOptionChange = (option) => (event) => { const oneDirectoryButton = document.getElementById('one-directory-button'); oneDirectoryButton.classList.add(option === 'one-directory' ? 'selected' : 'unselected'); oneDirectoryButton.classList.remove(option !== 'one-directory' ? 'selected' : 'unselected'); - updateCurrentCommandDisplay(); + void updateCurrentCommandDisplay(); }; const consoleWindowOptionChange = (option) => (event) => { @@ -37,12 +37,12 @@ const consoleWindowOptionChange = (option) => (event) => { const windowButton = document.getElementById('window-based-button'); windowButton.classList.add(option === 'window' ? 'selected' : 'unselected'); windowButton.classList.remove(option !== 'window' ? 'selected' : 'unselected'); - updateCurrentCommandDisplay(); + void updateCurrentCommandDisplay(); }; const iconLocationChange = async (event) => { const valid = await colourInput(event.target, true, true, false); - updateCurrentCommandDisplay(); + void updateCurrentCommandDisplay(); // If valid and a value exists, show the message if the file is not an ico file const warningElement = document.getElementById('icon-invalid-warning'); @@ -116,7 +116,7 @@ const recursionLimitToggle = (enabled) => { }; const rawArgumentsChange = (event) => { - updateCurrentCommandDisplay(); + void updateCurrentCommandDisplay(); }; const packageScript = async (event) => { @@ -131,7 +131,7 @@ const packageScript = async (event) => { } // Pre-checks - const currentConfiguration = getCurrentConfiguration(); + const currentConfiguration = await getCurrentConfiguration(); const entryScript = currentConfiguration.find((c) => c.optionDest === 'filenames').value; if (entryScript === '') { @@ -150,11 +150,11 @@ const packageScript = async (event) => { } // If checks have passed, package the script - startPackaging(); + await startPackaging(); }; -const openOutputFolder = (event) => { - const currentConfiguration = getCurrentConfiguration(); +const openOutputFolder = async (event) => { + const currentConfiguration = await getCurrentConfiguration(); const entryScript = currentConfiguration.find((c) => c.optionDest === 'filenames').value; const isOneFile = currentConfiguration.find((c) => c.optionDest === 'onefile').value; eel.open_output_in_explorer(getNonPyinstallerConfiguration().outputDirectory, entryScript, isOneFile)(); diff --git a/auto_py_to_exe/web/js/utils.js b/auto_py_to_exe/web/js/utils.js index b875300d..d350c052 100644 --- a/auto_py_to_exe/web/js/utils.js +++ b/auto_py_to_exe/web/js/utils.js @@ -37,6 +37,10 @@ const isFileAnIco = async (file_path) => { return await eel.is_file_an_ico(file_path)(); }; +const convertPathToAbsolute = async (path) => { + return await eel.convert_path_to_absolute(path)(); +}; + const chooseOptionString = (optionStrings) => { // Try not to use compressed flags if (optionStrings[0].length === 2 && optionStrings.length > 1) {