// Variables used by Scriptable. // These must be at the very top of the file. Do not edit. // icon-color: red; icon-glyph: magic; // shared by hwangbible // at: https://talk.automators.fm/t/tips-how-to-use-npm-modules-in-scriptable/5747 const fm = FileManager.iCloud(); const dir = fm.documentsDirectory(); const downloadIfNeeded = async (pkg, isAutoUpdateOn) => { let name = getPackageName(pkg); let filePath = fm.joinPath(dir, name + '.js'); let isInstalled = await isFound(filePath); // If the package exists and autoupdate is off, stop checking further if (isInstalled && !isAutoUpdateOn) { console.log(`'${name}' is already installed, and autoupdate is disabled! Proceeding to import from disk...`); return; } // Get the package information which satisfies the given semver range let versionInfo = await getStatus(pkg); let versions = versionInfo.satisfied; let version = versionInfo.highest; // Download the newer version if necessary if (isInstalled && isAutoUpdateOn) { let installedVersion = await getInstalledVersion(name); // Check if the installed version satisfies the semver range if (versions.includes(installedVersion)) { console.log(`'${name}@${installedVersion}' satisfies the requested version. Good to go!`); return; } else { console.log(`'${name}@${installedVersion}' doesn't match the version requested. Reinstalling '${version}' now...`); } } else { console.log(`'${name}' was never installed previously. Downloading now...`); } // Download the package source and save to disk let source = await getPackageSource(pkg); savePackageToDisk(name, version, source); }; const getInstalledVersion = async name => { // Read the version from {package}.ver let filePath = fm.joinPath(dir, name + '.ver'); let version; if (isFound(filePath)) { let content = fm.readString(filePath); if (/^\d+\.\d+\.\d+$/g.test(content)) { version = content; } } console.log(`The installed version of '${name}' is ${version}.`); return version; }; const getPackageSource = async pkg => { // Get the standalone package source from wzrd.in let request = new Request(`https://wzrd.in/standalone/${encodeURIComponent(pkg)}`); let response = await request.loadString(); return response; }; const getPackageName = pkg => { return pkg.split('@')[0]; }; const getStatus = async pkg => { // Retrieve the information about the package let request = new Request(`https://wzrd.in/status/${encodeURIComponent(pkg)}`); let response = await request.loadJSON(); // Fail if the response is not good if (response.statusCode >= 400 || response.ok === false) { throw response.message; } // Fail if the semver did not satisfy any versions available on npm // Otherwise, sort the versions in descending order let versions = response.builds && Object.keys(response.builds); if (versions.length < 1) { throw `'${pkg}' did not satisfy any versions available on npm!`; } else { versions.sort((a, b) => b.localeCompare(a, undefined, { numeric: true })); } // Get all the satisfied versions and the highest version let result = { highest: versions[0], satisfied: versions, }; return result; }; const isFound = async filePath => { // Check if the package is already downloaded if (fm.fileExists(filePath)) { return true; } // Sync with iCloud and check again await syncFileWithiCloud(filePath); if (fm.fileExists(filePath)) { return true; } return false; }; const savePackageToDisk = (name, version, source) => { // Write the package source and version info to disk let filename = fm.joinPath(dir, name); let jsFilePath = filename + '.js'; let versionFilePath = filename + '.ver'; let pkg = `${name}@${version}`; tryWriteFile(jsFilePath, source, pkg); tryWriteFile(versionFilePath, version, pkg); console.log(`Successfully installed ${name}@${version}!`); }; const syncFileWithiCloud = async filePath => { // Try to sync with iCloud in case the package exists only on iCloud try { console.log(`Attempting to sync with iCloud just in case...`); await fm.downloadFileFromiCloud(filePath); console.log(`Finished syncing ${filePath}`); } catch (err) { console.log(`${filePath} does not exist on iCloud.`); } }; const tryWriteFile = (path, content, pkg) => { // Sometimes wzrd.in is acting up and the file content is undefined. // So, here is a little trick to let you know what's going on. try { console.log(`Saving ${pkg} to disk at ${path}...`); fm.writeString(path, content); } catch (err) { throw `The package source from 'https://wzrd.in/standalone/${pkg}' is probably corrupted! Try with the different patch version.`; } }; module.exports = async (pkg, isAutoUpdateOn = false) => { let name = getPackageName(pkg); await downloadIfNeeded(pkg, isAutoUpdateOn); return importModule(`${name}`); };